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
#include
#include
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;
}