https://nanti.jisuanke.com/t/19931
(uva数据有问题,计蒜客可以交)
题目大意:
给你一笔钱,你有n个面值的硬币,问兑换这笔钱用多少个硬币,让硬币数量最少(若有多组答案,输出尽可能使用面额小的钱币)
题解:
完全背包+路径输出
dp[i]代表兑换面值为i的钱需要的最少硬币数量
对于第i个硬币,用还是不用(完全背包的思想)
dp[j+a[i]]=min(dp[j+a[i]],dp[j]+1)
然后因为要记录第几个硬币用几个,所以再开一个数组c[i][j]表示兑换面值为i的钱,第j个硬币用了几个
注意可能有些人会觉得题目要求让硬币数量最少,但是输出的时候又有一个要求是如果有多个解,输出尽可能使用面值小的硬币的方案,什么意思呢
就像第三个样例
43 5 1 2 21 40 80
答案是1 1 0 1 0
但我还可以 1 0 2 0 0
但这样显然1 1 0 1 0这组方案使用了更小的面值小的硬币,所以答案是它
这种选择就体现在if(dp[j+a[i]]>=dp[j]+1) 这个等于号上,如果使用了更多面值小的硬币,那么剩余的金额一定更大,所以一定后更新到
#include<bits/stdc++.h>
#include<cstring>
#define ll long long
using namespace std;
#define INF 0x3f3f3f3f
int a[20];
int dp[2010];
int c[2010][20];
int main()
{
//freopen("input.txt","r",stdin);
int n,v;
while(~scanf("%d%d",&v,&n))
{
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
memset(dp,INF,sizeof(dp));
memset(c,0,sizeof(c));
dp[0]=0;
for(int i=1;i<=n;++i)
for(int j=0;j<=v;++j)
if(j+a[i]<=v)
{
if(dp[j+a[i]]>=dp[j]+1)
{
dp[j+a[i]]=dp[j]+1;
for(int k=1;k<=n;++k)
{
c[j+a[i]][k]=c[j][k];
}
c[j+a[i]][i]++;
}
}
if(dp[v]==INF)
puts("-1");
else
{
for(int i=1;i<n;++i)
printf("%d ",c[v][i]);
printf("%d\n",c[v][n]);
}
}
return 0;
}