POJ 1787 Charlie's Change 最多金币

来源:互联网 发布:数据科学考研科目 编辑:程序博客网 时间:2024/06/05 11:56

原题: http://poj.org/problem?id=1787

题目:

Charlie’s Change
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 3520 Accepted: 1043
Description

Charlie is a driver of Advanced Cargo Movement, Ltd. Charlie drives a lot and so he often buys coffee at coffee vending machines at motorests. Charlie hates change. That is basically the setup of your next task.

Your program will be given numbers and types of coins Charlie has and the coffee price. The coffee vending machines accept coins of values 1, 5, 10, and 25 cents. The program should output which coins Charlie has to use paying the coffee so that he uses as many coins as possible. Because Charlie really does not want any change back he wants to pay the price exactly.
Input

Each line of the input contains five integer numbers separated by a single space describing one situation to solve. The first integer on the line P, 1 <= P <= 10 000, is the coffee price in cents. Next four integers, C1, C2, C3, C4, 0 <= Ci <= 10 000, are the numbers of cents, nickels (5 cents), dimes (10 cents), and quarters (25 cents) in Charlie’s valet. The last line of the input contains five zeros and no output should be generated for it.
Output

For each situation, your program should output one line containing the string “Throw in T1 cents, T2 nickels, T3 dimes, and T4 quarters.”, where T1, T2, T3, T4 are the numbers of coins of appropriate values Charlie should use to pay the coffee while using as many coins as possible. In the case Charlie does not possess enough change to pay the price of the coffee exactly, your program should output “Charlie cannot buy coffee.”.
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.

思路:

输入需要凑齐的面值P,输入四种货币的数目,面值分别为1,5,10,25,求出一种可以凑齐P的方案,要求使用的金币数最多。

显然,这种有数量限制的货币我们可以看作是多重背包,就按多重背包的处理方式求最大货币数。至于难点如何求出最大具体的方案,我们只需要用一个path[]数组保存过程就可以了。

 path[j]=j-value[i];   

上式代表这当容量为j的时候,path[j]保存的是用了此物品剩下的金额。
反过来推,就有value[i]=path[j]-j。

 while(path[i]!=-1)      {      ans[i-path[i]]++;      i=path[i];}

这里就是利用上面的反推结果,每次能得到当前状态最后一次放入的物品的金额。
i表示当前剩余价值,直到最后一个都没放的时候,就有i=0,path[i]!=1就是搜索结果。
然后就可以输出方案了。

代码:

#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>using namespace std;const int INF = 10000000;const int N = 10005;int value[5]={0,1,5,10,25};int num[5];     //记录每种货币的数目int dp[N];      //记录当前容量的最大数目int ans[26];    //ans[25]表示面值为25的数目int used[N];    //记录该次操作的该物品使用了几次int path[N];    //用于反推结果int n;          //要求的金额void init(){    memset(num,0,sizeof(num));    memset(path,0,sizeof(path));    memset(num,0,sizeof(num));    memset(ans,0,sizeof(ans));    for(int i=0;i<=n+1;i++)    {        dp[i]=-INF;                 //将所有金额下需要的货币初始化无穷小,如果不存在满足的方案,那dp[总金额]=无穷小;    }    path[0]=-1;                     //用于反推的时候找到终点,path[0]即为不需要在增加货币数了。    dp[0]=0;}void pack(){    for(int i=1;i<=4;i++)    {        memset(used,0,sizeof(used));//used数组是对当前物品的使用次数进行计数,所以每次变更货币就需要清零一次        for(int j=value[i];j<=n;j++)        {            //显然,一维dp第二重循环表示的是完全背包            //j-value[i]表示的是用第i个货币去先装入背包的剩下金额            //used[j-value[i]]<num[i]表示如果本次不放,已放的件数必须要比总数小,        //used[j-value[i]]<=num[i]-1,这样才能有物品放进去            //dp[j-value[i]]>=0表示前一次的状态可以实现,如果不满足即代表dp[j-value[i]]=-INF,        //视为前面的货币凑不够此状态,后面的计算就不能用这个结果。            //dp[j]<dp[j-value[i]]+1表示放了此物品的数目一定要比不放的情况下数目要多,否则不是最优解            if(dp[j]<dp[j-value[i]]+1&&dp[j-value[i]]>=0&&used[j-value[i]]<num[i])            {                dp[j]=dp[j-value[i]]+1;     //放了此物品,等于此物品的个数1加上剩下容量能放的最大个数。                used[j]=used[j-value[i]]+1; //放了此物品的个数等于不放此物品的时候放的个数加1                path[j]=j-value[i];         //用于记录求最优解具体状态,即当前容量放了此物品之后剩下的容量            }        }    }}void findans(){    if(dp[n]<0)    {          printf("Charlie cannot buy coffee.\n");          return;    }    else    {        int i=n;        while(path[i]!=-1)              {            ans[i-path[i]]++;            i=path[i];        }        printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",        ans[value[1]],ans[value[2]],ans[value[3]],ans[value[4]]);    }}int main(){    while(scanf("%d",&n)!=EOF)    {        int sum=n;        init();                         //对于操作的每次测试都需要初始化        for(int i=1;i<=4;i++)        {            scanf("%d",&num[i]);            sum=sum+num[i];        }        if(sum==0)  break;              //因为各项都为大于等于0的数,其和为0即所有数都为0        pack();        findans();    }    return 0;}
0 0
原创粉丝点击