浙大PAT CCCC L3-001 湊零錢 ( 0/1背包 && 路徑記錄 )
阿新 • • 發佈:2018-03-20
esp AR pre 倒序輸出 情況 sof font ron src
題目鏈接
分析 :
就是一個 0/1 背包,但是需要記錄具體狀態的轉移情況
這個可以想象成一個狀態轉移圖,然後實際就是記錄路徑
將狀態看成點然後轉移看成邊,最後輸出字典序最小的路徑
這裏有一個很巧妙的做法
先將所有的硬幣升序排序(這一點很重要)
然後在這一條件下,假設當前狀態是考慮第 i 個硬幣,前一個狀態是考慮第 i-1 個硬幣
試想對於同一個體積,如果選用的硬幣數量越多是不是字典序更小
然後對於如果對於同一體積下,選用硬幣數一樣多的兩種方案
由於我們已經升序排序,如果有一樣多硬幣的情況,那麽永遠選後面面值大的更新答案
因為體積固定嘛,那麽後面選用的硬幣面值更大,湊成一樣的體積,是不是說明
其他的選用硬幣應該更小,也就是字典序更小了?
#include<bits/stdc++.h> using namespace std; const int maxn = 1e4 + 10; const int INF = 0x3f3f3f3f; struct Status{ int Coin, Pre; }st[maxn];///記錄狀態 int dp[maxn], Coin[maxn]; int N, M; int main(void) { while(~scanf("%d %d", &N, &M)){ for(int i=0; i<N; i++) scanf(View Code"%d", &Coin[i]); sort(Coin, Coin+N); for(int i=0; i<=M; i++) st[i].Coin = st[i].Pre = -1, dp[i] = -INF;///初始化為負無窮小,對於背包是否剛剛好裝滿 ///一般都選擇這樣初始化 dp[0] = 0; for(int i=0; i<N; i++){ for(int j=M; j>=Coin[i]; j--){if(dp[j - Coin[i]] + 1 >= dp[j]){///對於 == 的情況,可以去用題目給的第一個樣例, ///然後去掉這個等號輸出答案,再想一下為什麽錯了,可能理解就更多了 dp[j] = dp[j - Coin[i]] + 1; st[j].Coin = Coin[i];///記錄當前狀態是選了哪個硬幣 st[j].Pre = j - Coin[i];///記錄從哪個狀態轉移過來 } } } if(dp[M] > 0){ vector<int> ans; int now = M; while(st[now].Coin != -1){///倒序回復路徑 ans.push_back(st[now].Coin); now = st[now].Pre; } for(int i=ans.size()-1; i>=0; i--){///倒序輸出倒序路徑就是正確路徑了 printf("%d", ans[i]); if(i != 0) putchar(‘ ‘); }puts(""); }else puts("No Solution"); } return 0; }
浙大PAT CCCC L3-001 湊零錢 ( 0/1背包 && 路徑記錄 )