题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805054207279104
题意:给定n枚硬币的面值,需要支付金额m,求能否恰好支付,若能,输出最小序列,若不能输出No Solution
思路:很容易看出这是一个完全背包问题,把每枚硬币的体积视为其面值,价值也视为其面值,则题目就转换成在背包空间为m时能否刚好装满,用dp[i][j]表示前i枚硬币装在空间为j的背包中最大价值。但题目的难点在于需要输出最小序列,我们可以这样做,输入数据之后按降序排列,然后从i=1到i=n进行dp,然后输出从i=n到i=1回溯,这样的结果自然就是序列最小的。还有一点需要注意的就是:
if(dp[j]<=dp[j-a[i]]+a[i])
dp[j]=dp[j-a[i]]+a[i],b[i][j]=true;
这里必须是‘<=',这样才能保证序列最小。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=10005; 5 int a[maxn],dp[105],res[maxn],k,n,m; 6 bool b[maxn][105]; 7 8 bool cmp(int x,int y){ 9 return x>y; 10 } 11 12 int main(){ 13 scanf("%d%d",&n,&m); 14 for(int i=1;i<=n;++i) 15 scanf("%d",&a[i]); 16 sort(a+1,a+n+1,cmp); 17 for(int i=1;i<=n;++i) 18 for(int j=m;j>=a[i];--j) 19 if(dp[j]<=dp[j-a[i]]+a[i]) 20 dp[j]=dp[j-a[i]]+a[i],b[i][j]=true; 21 if(dp[m]!=m) 22 printf("No Solution "); 23 else{ 24 int v=m,t=n; 25 while(v){ 26 if(b[t][v]){ 27 res[k++]=a[t]; 28 v-=a[t]; 29 } 30 --t; 31 } 32 printf("%d",res[0]); 33 for(int i=1;i<k;++i) 34 printf(" %d",res[i]); 35 printf(" "); 36 } 37 return 0; 38 }