Description
Input
Output
Sample Input
12 5 3 1 2 16 0 0 0 1 0 0 0 0 0
Sample Output
Throw in 2 cents, 2 nickels, 0 dimes, and 0 quarters. Charlie cannot buy coffee.
题意:给出总价值,然后给出价值为1,5,10,25的价值的钱币的数量,问能够达到需求的总价值需要的金钱数目最多的方案
思路:乍一看是多重背包,事实上也确实是多重背包,但是由于要输出方案,所以我一直没有想到方法,看了人家的代码,才知道别人记录路径的方法挺神奇的
详细注释在代码中
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int N = 10010; int dp[100000],sum,v[5] = {1,5,10,25},ans[30],path[10010]; //dp数组装的是存放钱的总数目,ans则是每种钱的数目,path用来记录路径 void ZeroOnePack(int pos,int k) { int i; for(i = sum;i>=k*v[pos];i--) { if(dp[i-k*v[pos]]+k>dp[i]) { dp[i] = dp[i-k*v[pos]]+k; path[i] = pos*N+(i-k*v[pos]); } } } void CompletePack(int pos) { int i; for(i = v[pos];i<=sum;i++) { if(dp[i-v[pos]]+1>dp[i]) { dp[i] = dp[i-v[pos]]+1; path[i] = pos*N+(i-v[pos]); } } } void MultiplePack() { int i,j,k; for(i = 0;i<4;i++) { if(v[i]>sum) return ; if(v[i]*ans[i]>=sum) CompletePack(i); else { int cnt = ans[i]; for(j = 1;j<=cnt;j*=2) { ZeroOnePack(i,j); cnt-=j; } if(cnt) ZeroOnePack(i,cnt); } } } int main() { int i,j,k; while(~scanf("%d",&sum),sum) { for(i = 0;i<4; i++) scanf("%d",&ans[i]); for(i = 1; i<=sum; i++) dp[i] = -N; dp[0] = 0; MultiplePack(); if(dp[sum]<0) { printf("Charlie cannot buy coffee.\n"); continue; } memset(ans,0,sizeof(ans)); while(sum) { /*由01背包中的path[i] = pos*N+(i-k*v[pos]);可知,s1相当于pos s2相当于i-k*v[pos] 而完全背包pos*N+(i-v[pos]);中,s1相当于pos s2相当于i-v[pos] */ int s1 = path[sum]/N;//记录现在改放的是多少钱 int s2 = path[sum]%N;//记录剩余钱数 ans[v[s1]]+=(sum-s2)/v[s1]; sum = s2; } printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[5],ans[10],ans[25]); } return 0; }