一開始沒多想,雖然注意到數據N<=10^4的范圍,想PAT的應該不會超時吧,就理所當然地用dfs做了,結果最后一組真的超時了。
剪枝啥的還是過不了,就意識到肯定不是用dfs做了。
直到看到別人說用01背包的思路,果真好久沒做題了智力水平下降,且原本dp就是我的弱項,壓根就沒把這題往dp上去想額。。。
(http://www.liuchuo.net/archives/2323)
題意:從n個硬皮中選取方案,使得總和價值正好為m,如果有多種,方案為排列最小的那個。
可以把硬幣看成w=v(即容量=價值)的物品,現在要選取這些物品放入到容量為m的背包中,求能裝的最大價值。
如果最大價值恰好等於容量m,那么方案則是可行的,否則輸出No Solution。
由於要輸出排列最小的方案,所以先將硬幣按價值從大到小排列,相當於我先裝大的,再裝小的。
接着用01背包的方法求dp[j]。
chosen[i][j]數組表示背包里容量為j的時候,是否含有第i個物品,用於最后序列的輸出。

#include <iostream> #include <cstdio> #include <algorithm> #include <string.h> #include <vector> #define INF 0x3f3f3f3f using namespace std; const int maxn=10000+4; const int maxv=105; int coins[maxn]; int dp[maxv]; //dp[v]表示:總和加起來=v的序列中最后一個值最小為多少。 int chosen[maxn][maxv]; int ans[maxn]; int cnt=0; bool cmp(int a,int b){ return a>b; } int main() { int n,m; scanf("%d %d",&n,&m); for(int i=0;i<n;i++){ scanf("%d",&coins[i]); } sort(coins,coins+n,cmp); dp[0]=0; for(int i=0;i<n;i++){ for(int j=m;j>=coins[i];j--){ if(dp[j]<=dp[j-coins[i]]+coins[i]){ dp[j]=dp[j-coins[i]]+coins[i]; //printf("i:%d j:%d dp:%d chosen:%d\n",i,j,dp[j],coins[i]); chosen[i][j]=1; } } } if(dp[m]!=m){ printf("No Solution"); } else{ int v=m,idx=n-1; while(v){ if(chosen[idx][v]){ ans[cnt++]=coins[idx]; v-=coins[idx]; } idx--; } printf("%d",ans[0]); for(int i=1;i<cnt;i++) printf(" %d",ans[i]); } return 0; }
PS:想了解背包問題的,有個《背包九講》,網上可以搜搜看看,挺好的