解题思路:
一开始想用搜索+回溯来做,但发现最后一个样例过不去,也想不出该怎么剪枝,就考虑其他方法了。
仔细分析一下就是一个01背包问题,只不过物品质量和价值比为1:1,求解就简单了。
如果最后结果dp[n][m]不等于价值量,也就是无解了。
第二个问题是求最小解,一开始我是从小到大放,但是无法求出最小解,就试着从大到小放硬币,发现可以完成任务。
代码如下:
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 10000 + 5;
int coin[maxn], n, m;
int dp[maxn][101];
int choice[maxn][101];
vector<int> ans;
bool cmp(int a, int b) //从大到小
{
return a > b;
}
void save(int n, int m) //一开始用来递归打印的函数,现在干脆直接拿来存答案了
{
if(choice[n][m] == -1) return;
else if(choice[n][m] == 0) save(n-1, m);
else if(choice[n][m] == 1)
{
save(n-1, m-coin[n]);
ans.push_back(coin[n]);
}
}
int main()
{
while(scanf("%d%d", &n, &m) == 2)
{
for(int i = 1; i <= n; i++) scanf("%d", &coin[i]);
sort(coin+1, coin + n + 1, cmp);
for(int i = 0; i <= n; i++) dp[i][0] = 0;
for(int i = 0; i <= m; i++) dp[0][i] = 0;
choice[0][0] = -1;
for(int i = 1; i <= n; i++)
{
for(int j = m; j >= 1; j--)
if(j >= coin[i])
{
if(dp[i-1][j] > dp[i-1][j-coin[i]] + coin[i])//如果算上当前硬币小于不算的情况,就不选
{
dp[i][j] = dp[i-1][j];
choice[i][j] = 0;
}
else //算上大于等于就选当前硬币
{
dp[i][j] = dp[i-1][j-coin[i]] + coin[i];
choice[i][j] = 1;
}
}
else
{
dp[i][j] = dp[i-1][j];
choice[i][j] = 0;
}
}
if(dp[n][m] == m)
{
ans.clear();
save(n, m);
for(int i = ans.size()-1; i >= 0; i--)
if(i == ans.size()-1) printf("%d", ans[i]);
else printf(" %d", ans[i]);
printf("\n");
}
else printf("No Solution\n");
}
return 0;
}