题意:给定不同面额的硬币若干个,要求找出一个硬币序列Vi满足V1 <= V2 <= ... <= Vk such that V1 + V2 + ... + Vk = M.
分析:此题有些类似我们之前讲过的01背包问题。若找出满足题意得多个序列,选择最小的序列。我们设F(n,m)为前n个硬币所能获得的最大的(但不大于m)的硬币数额之和。若给定的N,M情况下,有F(N,M)==M,则有解,否则无解。
状态方程:F(n,m)=max{F(n-1,m),F(n-1,m-num[n])+num[n]}
代码如下:
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
int dp[10005][105];
int flag[10005][105];
int num[10005];
bool cmp(int a,int b){
return a>b;
}
int calculate(int n,int m){
for(int i=1;i<n+1;i++){
for(int j=1;j<=m;j++){
if(j<num[i]||dp[i-1][j-num[i]]+num[i]<dp[i-1][j])//j<num[i],说明只能从前i-1个里面找满足不大于j的最大数额
dp[i][j]=dp[i-1][j];
else{
dp[i][j]=dp[i-1][j-num[i]]+num[i];
flag[i][j]=1;
}
}
}
return dp[n][m];
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%d",num+1+i);
sort(num+1,num+1+n,cmp);
memset(dp,0,sizeof(dp));
memset(flag,0,sizeof(flag));
int t=calculate(n,m);
if(t!=m){
printf("No Solution\n");
}
else{
int first=1;
while(m){
while(!flag[n][m])
n--;
if(first){
printf("%d",num[n]);
first=0;
}
else
printf(" %d",num[n]);
m=m-num[n];
n--;
}
}
return 0;
}