题目
大意
题目其实就是0-1背包问题的一个简单变种,求解答案是否存在其实不难,难的就是对答案矩阵的理解。先看一下代码。
#include <cstdio>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
int arr[(int)1e4+5];
int dp[(int)1e4+5][105] = {0};
int x[(int)1e4+5][105];
vector<int> ans;
bool cmp(int a, int b) {
return a > b;
}
int main(void)
{
int N, M;
scanf("%d%d", &N, &M);
for(int i = 1; i <= N; ++i) {
scanf("%d", &arr[i]);
}
sort(arr+1, arr+N+1, cmp);
for(int i = 1; i <= N; ++i) {
for(int j = 1; j <= M; ++j) {
if(j >= arr[i]) {
dp[i][j] = max(dp[i-1][j], dp[i-1][j-arr[i]]+arr[i]);
if(dp[i-1][j-arr[i]]+arr[i] >= dp[i-1][j]) // 后面的选择会覆盖前面的
x[i][j] = 1;
}
else {
dp[i][j] = dp[i-1][j];
}
}
}
if(dp[N][M] != M)
puts("No Solution");
else {
int j = M;
for(int i = N; i >= 1; --i) {
if(x[i][j]) {
ans.push_back(arr[i]);
j -= arr[i];
}
}
bool flag = true;
for(auto i : ans) {
if(x[i]) {
if(flag) flag = false;
else printf(" ");
printf("%d", i);
}
}
}
return 0;
}
和0-1背包问题不同的是这里对硬币的金额做了一个从大到小的排序,由题意可知我们要找的最小的序列其实就是尽可能用小金额的硬币构成的序列。
求解过程中我们把每次数额更新的位置存起来,然后通过上面的代码倒推就能求出最小的序列了。