问题1:dp[i]表示背包最大容量为i的情况下,能装下的最多价值。
由于完全背包中,每个物品可以选择无数次。一种比较简单的思路是,每一个物品都copy足够多次,多到单独选择任意一个物品都能够填满整个背包。这样将物品数量增加,将问题转换为0 1背包。但是这种思路会被卡。
另外的思路:考虑在0 1背包中,是如何保证物品只被选择一次呢?即重量3的物品在容量为10中能够被选择,是建立在其在容量为7时没有被选择。即转移方程中 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - a[i]] + b[i]);是将dp[i - 1]时的状态,即考虑前i - 1个物品时的状态下的值作为选择。在dp[i - 1]的所有状态中,都没有选择i物品。
如果理解了这一点,那么如何多次选择就很明显了,即将dp[i - 1]改为dp[i]既可。使用空间优化后的数组,仅仅需要将0 1背包中,之前从右往左的计算改为从左往右既可。
问题2:同理,dp[i]表示背包刚好装满i的情况下,能装下的最多价值。同样从左往右遍历既可。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, v;
cin >> n >> v;
vector<int> a(n), b(n);
for (int i = 0; i < n; i++)
cin >> a[i] >> b[i];
vector<int> dp1(v + 1), dp2(v + 1);
for (int i = 0; i < n; i++) {
for (int j = 0; j <= v; j++) {
if (a[i] < j) {
dp1[j] = max(dp1[j], dp1[j -a[i]] + b[i]);
if (dp2[j - a[i]] != 0) dp2[j] = max(dp2[j], dp2[j - a[i]] + b[i]);
} else if (a[i] == j) {
dp1[j] = max(dp1[j], dp1[j -a[i]] + b[i]);
dp2[j] = max(dp2[j], b[i]);
}
}
}
cout << dp1[v] << '\n' << dp2[v] << '\n';
}
// 64 位输出请用 printf("%lld")



京公网安备 11010502036488号