欢迎访问我的pat甲级题解目录哦
题目描述
算法设计
这是一道0-1背包问题。设 表示第 个硬币的面值, 表示在第 个硬币中,任选一些硬币,是否能使总面值恰好为 。假设所有硬币下标从1开始,那么边界条件为 ,表示不选任何硬币的总面值为0。状态转移方程为
-
(
||
是或关系运算符)
题目要比普通的0-1背包问题稍微麻烦一些,因为它要求打印字典序最小的解。为此,我们需要额外定义一个二维数组
,
表示在组成总面值为
的硬币中,第
个硬币是否被选中。我们进行递推时,下标从1~n
进行递推。这时,将硬币按照面值大小从大到小排序,如果有解,我们按下标从n~1
进行打印即可。
C++代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int> v(n + 1);
vector<bitset<105>> dp(n + 1), select(n + 1);
for (int i = 1; i <= n; ++i)
cin >> v[i];
sort(v.begin() + 1, v.end(), greater<int>());
dp[0][0] = select[0][0] = true;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= m; ++j) {
dp[i][j] = dp[i - 1][j];
if (j >= v[i]) {
dp[i][j] = dp[i][j] || dp[i - 1][j - v[i]];
if (dp[i - 1][j - v[i]])
select[i][j] = true;
}
}
}
if (!dp[n][m]) {
cout << "No Solution";
} else {
bool output = false;
for (int i = n; i >= 1; --i)
if (select[i][m]) {
cout << (output ? " " : "") << v[i];
m -= v[i];
output = true;
}
}
return 0;
}