Luogu P5020 貨幣系統

2022-05-09 19:42:51 字數 1443 閱讀 4851

作為一道 noip 題,當然要考慮從部分分到正解,一是拿穩分防止正解寫掛,二是可以拿不同部分分的程式對拍。

分析:看到樣例大膽猜想,或者考慮這個問題的本質。

可以通過反證法證明新貨幣系統的集合一定是原貨幣系統的子集。

考慮先把 \(a\) 系統的數全部選上,然後刪去一些,則這些刪去的一定可以被剩下的數都表示出來,否則這個不能被新貨幣系統表示出來的原貨幣系統的數本身不能被表示出來,而這個數所拓展出去的數也不一定能被表示出來。

下面設 \(m=\max\\)

直接狀壓選哪些數,然後跑一遍 \(\mathcal(nm^2)\) 的 \(dp\) (當然實現的好的話可以做到 \(\mathcal(nm)\)),然後遍歷不在當前貨幣系統的數,看是否能被表示出來,可以的話就更新答案。

減少常數的方法:類似最優性剪枝,如果當前的狀態的二進位制數本來就大於等於答案,一定不會比答案更優,可以選擇不跑 \(dp\)。

總時間複雜度 \(\mathcal(t2^nnm^2)\) 或 \(\mathcal(t2^nnm)\)。

畢竟和位置沒有關係,所以先排一下序看看有什麼結論。

這個時候發現,\(a_1\) 是一定要選的,因為 \(a_1\) 是最小的數,因為新貨幣系統中選的最小的數一定小於等於它,如果選比它更小的數,會使得比它更小的數能被新貨幣系統表示出來,不符合題意,所以一定選 \(a_1\)。

然後對於 \(a_1\),去更新它的倍數,也就是它的倍數在新貨幣系統中已經不需要了。然後對於後面的 \(a_i\),如果可以被前面的數表示過了,就一定不會出現在新貨幣系統中。否則在所有可以被表示的數上加上它的倍數去轉移乙個數是否能被表示出來。

時間複雜度 \(\mathcal(tnm^2)\),考慮優化,在轉移的時候 \(\bmod a_i\) 相等的位置會被同樣的轉移多次,所以只對每個不同的 \(\bmod a_i\) 去 \(dp\) 轉移即可,一次轉移是 \(\frac\),一共 \(a_i\) 次,這樣對於乙個 \(a_i\) 的單次轉移複雜度就到 \(\mathcal(m)\) 了=w=

總時間複雜度 \(\mathcal(tnm)\)

#include#include#includeinline int min(int x, int y) 

inline int max(int x, int y)

inline int read()

const int n = 110;

const int k = 25010;

int n, a[n];

int b[n], cnt;

bool vis[k], vis2[k];

int main()

for(int i = 1; i <= n; ++i)

for(int j = 0; j < a[i]; ++j) vis2[j] = 0;

} printf("%d\n", cnt);

}return 0; }

Luogu P5020 貨幣系統

先把 a 陣列排一下序。從最小的數開始選,顯然最小這個數必須選,然後利用完全揹包的思想,從 a i 到最大值篩選一遍,將可以組成的打上標記。在判斷後面的數字時,如果已經被標記過了,就不再選,沒有被標記過就標記一下,再篩選一次數 即再做一次完全揹包 include define n 110 defin...

題解 luogu P5020 貨幣系統

題目鏈結 玄學題目。可以用篩表,動規,搜尋做 篩表從小到大列舉,篩掉可以表示出來的數,剩下的數就是必須要選的 也就是答案 includeusing namespace std int dp 25005 t,a 105 n,sum intmain sort a 1,a 1 n for int i 1 ...

洛谷 5020 貨幣系統

題目描述 在的國度中共有 n 種不同面額的貨幣,第 iii 種貨幣的面額為 a i 你可以假設每一種貨幣都有無窮多張。為了方便,我們把貨幣種數為 n 面額陣列為 a 1 n 的貨幣系統記作 n,a 在乙個完善的貨幣系統中,每乙個非負整數的金額 都應該可以被表示出,即對每乙個非負整數 x,都存在 n ...