题目链接:
https://vjudge.net/contest/348156#problem/B
题面:
翻译:
查理是高级货物运输公司的司机,查理经常开车,所以他经常在高速公路的咖啡自动售货机买咖啡。查理讨厌改变。这基本上就是你下一个任务的设置。
你的程序将给出数字和类型的硬币查理和咖啡价格。咖啡自动售货机接受1美分、5美分、10美分和25美分的硬币。程序应该输出查理必须使用哪些硬币支付咖啡,以便他使用尽可能多的硬币。因为查理真的不想拿回任何零钱,他想付出确切的代价。
输入:
输入的每一行包含五个整数,用一个空格分隔,描述要解决的一种情况。P行的第一个整数,1<=P<=10000,是咖啡的价格(单位:美分)。接下来的四个整数,C1,C2,C3,C4,0<=Ci<=10000,是Charlie选择的美分,五美分,十美分和四分之一(25美分)的数字。输入的最后一行包含五个零,不应为其生成输出。
输出:
对于每种情况,您的程序都应该输出一行,其中包含字符串“Throw in T1 cents,T2 nickels,T3 dimes,and T4 quarters.”,其中T1,T2,T3,T4是查理在使用尽可能多的硬币支付咖啡时应该使用的适当值的硬币数。如果查理没有足够的零钱来支付咖啡的价格,你的程序应该输出“Charlie cannot buy coffee.”。
思路:
题目大概讲的就是有1分,5分,10分,25分的硬币,分别有C1,C2,C3,C4个,咖啡价格为P,求凑成p所用的最大硬币数。(完全背包,再记录一下路径)
开几个数组,分别记录容量j里的所用硬币数,使用某种分值的硬币数(判断是否超出题目所给的数量),以及分隔情况(求解每种分值的硬币数)
这是调试的过程,这样子看是不是更容易理解下面的代码呢,我们是始终要保存达到当前金额时花费的硬币数目是最多的,而a数组的作用是判断有没有超过所给的对应硬币的数量,而b数组则是用于记录路径的,来判断每个时候用的哪些金币,这个题目还是要看自己理解吧,讲我是真的讲的讲不清楚,我都观看大佬的博客研究了好久。。。。
参考代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
int w[10]= {1,5,10,25};
int dp[10000];
int t[10];
int a[100000];
int b[100000];
int c[100000];
int p,i,j;
int main()
{
while(scanf("%d",&p)!=EOF)//多组输入
{
int sum=p;
for(i=0; i<4; i++)
{
scanf("%d",&t[i]);
sum=sum+t[i];
}
if(sum==0)//判断输出5个零的时候跳出循环
{
break;
}
else
{
memset(dp,-1,sizeof(dp));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
dp[0]=0;
b[0]=-1;
for(i=0; i<4; i++)
{
memset(a,0,sizeof(a));
for(j=w[i]; j<=p; j++)
{
if(dp[j-w[i]]>=0&&dp[j-w[i]]+1>dp[j]&&a[j-w[i]]<t[i])
{
dp[j]=dp[j-w[i]]+1;//dp[j]存j里用多少枚硬币换。(要多的)
a[j]=a[j-w[i]]+1;//a[j]来记录是否超过数量
b[j]=j-w[i];//b[j]来记录路径
}
}
}
if(dp[p]<=0)//如果dp[p]小于就说明他的美元不足以购买coffee
{
printf("Charlie cannot buy coffee.\n");
}
else
{
while(b[p]!=-1)//顺着路径来查找用了哪些硬币
{
c[p-b[p]]++;
p=b[p];
}
printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",c[1],c[5],c[10],c[25]);
}
}
}
}