ZOJ 2156 Charlie's Change 多重背包(带路径)

题意:给定一个面值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代码:

View Code
#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;
}

 

你可能感兴趣的:(ACM_DP)