题目:
用几种面额的钱凑一个面额,问最少需要多少枚硬币。数量相同时尽量使用小面额,输出每个硬币用多少枚。输入的硬币已经按面额小到大排好序。
分析:
完全背包+路径输出。
因为要尽可能使用小面额的硬币,所以在转移的时候:
- 如果 f[j-a[i]] + 1 < f[j],肯定是要转移,记录状态j用的i硬币转移来的。
- 如果 f[j-a[i]] + 1 = f[j],也要转移,因为f[j]不变的情况下,越往后硬币 i 面额越大,那也就意味着前面用了更小的面额。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
typedef unsigned long long ull;
typedef long long ll;
const int MAXN=2050;
const double EPS=1e-8;
const int INF=0x3f3f3f3f;
const int MOD = 1e9+7;
int f[MAXN],n,V,a[MAXN],num[MAXN],pre[MAXN];
int main(){
// freopen("1.txt","r",stdin);
ios::sync_with_stdio(false);
while(cin >> V >> n){
for(int i=1;i<=n;i++){
cin >> a[i];
}
ms(num,0);
f[0] = 0;
pre[0] = 0;
for(int i=1;i<=V;i++) f[i] = INF;
for(int i=1;i<=n;i++){
for(int j=a[i];j<=V;j++){
// f[j] = min(f[j], f[j-a[i]]+1);
if(f[j-a[i]] + 1 <= f[j]){
f[j] = f[j-a[i]]+1;
pre[j] = i;
}
}
}
if(f[V] == INF) cout << "-1\n";
else{
while(V){
num[pre[V]]++;
V -= a[pre[V]];
}
for(int i=1;i<=n;i++){
cout << num[i] << " \n"[i==n];
}
}
}
return 0;
}