题意:给定一个面值Price, 1美分的硬币有c[1]个, 5美分的硬币有c[2]个,10美分的硬币有c[3]个,25美分的硬币有c[4]个,求用给定的硬币兑换Price面额的钱,并且要求硬币数最多,求出每种硬币得用多少个.
题目关键:要保存路径,然后输出。
我们用pre[j]表示最新更新j的那个状态,j-w[i]等状态,因为多重背包里有二进制压缩,有些状态转移1次纸币数量不一定只加了1,所以用num[j]表示状态转移到j加了多少张纸币。
路径输出处理:
memset(ans,0,sizeof(ans)); for(i=m;i!=0;i=pre[i]) //用pre[j]把路径逆着走一边 { int k= (i-pre[i])/num[i];//状态转移的纸币面额。 for(j=1;j<=4;j++) if(k==w[j])ans[j]+=num[i]; }
AC代码:
#include#include<string.h> #include using namespace std; int w[5]={0,1,5,10,25},c[5]; //我从1开始取的 int dp[10003]; int pre[10003],num[10003]; int m; int ans[5]; //保存答案 int main() { int i, j; while(~scanf("%d",&m)) { //判断终止数据 bool flag=1; for(i=1;i<=4;i++) { scanf("%d",&c[i]); if(c[i])flag=0; } if(flag&&!m)break; memset(dp,-1,sizeof(dp));//初始化-1,-1表示空值 dp[0]=0; memset(pre,-1,sizeof(pre)); //多重背包 for(i=1;i<=4;i++) if(c[i]*w[i]>=m) { for(j=w[i];j<=m;j++) if(dp[j-w[i]]!=-1&&dp[j] 1) //判断是否为-1,是-1则无需更新。 { dp[j]=dp[j-w[i]]+1; pre[j]=j-w[i]; num[j]=1; } } else { int k = 1; while(k<c[i]) { for(j=m;j>=w[i]*k;j--) if(dp[j-w[i]*k]!=-1&&dp[j] k) { dp[j]=dp[j-w[i]*k]+k; pre[j]=j-w[i]*k; num[j]=k; } c[i]-=k; k*=2; } for(j=m;j>=w[i]*c[i];j--) if(dp[j-w[i]*c[i]]!=-1&&dp[j] c[i]) { dp[j]=dp[j-w[i]*c[i]]+c[i]; pre[j]=j-w[i]*c[i]; num[j]=c[i]; } } if(dp[m]==-1) printf("Charlie cannot buy coffee.\n"); else { //构造解 memset(ans,0,sizeof(ans)); for(i=m;i!=0;i=pre[i]) //用pre[j]把路径逆着走一边 { int k= (i-pre[i])/num[i];//状态转移的纸币面额。 for(j=1;j<=4;j++) if(k==w[j])ans[j]+=num[i]; } printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[2],ans[3],ans[4]); } } return 0; }