这是一个经典的01背包,而背包问题的本质就是动态规划,因此完全不用管什么背包不背包的,记不住还影响心情。
接下来将从两个角度来解决这道动态规划。
角度一
令
d
p
i
dp_i
dpi 代表容量为
i
i
i 的背包能够装入的最大体积。
考虑逐个物品添加。
最开始什么也没装,所以全部初始化为
0
0
0 。
每一轮添加物品至容量为
i
i
i 的背包都有两种选择——装入该物品和不装入该物品,二者取最大值即可。
- 若装入该物品(假定其体积为 v v v ),我们可以事先装入该物品,问题就变成了寻找容量为 i − v i-v i−v 的背包能够装入的最大体积。
- 若不装入,保持 d p i dp_i dpi 不变即可。
- 综上 , d p i = m a x { v + d p i − v , d p i } dp_i=max\{v+dp_{i-v}, dp_i\} dpi=max{v+dpi−v,dpi} (注意这里的 d p i − v dp_{i-v} dpi−v 必须是上一轮的结果,如果用该轮结果,物品可能被添加两次)。
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e4+4;
int dp[N]; // dp[i]容量为i的背包能装入的最大空间
int main()
{
int V;
int n;
scanf("%d%d", &V, &n);
while (n--) {
int x;
scanf("%d", &x);
// 为保证dp[j-x]总是上一轮结果,采用倒序遍历
for (int j = V; j >= x; --j) {
dp[j] = max(dp[j], dp[j-x]+x);
}
}
printf("%d", V-dp[V]);
return 0;
}
角度二
令
d
p
i
dp_i
dpi 代表容量为
i
i
i 的背包能够最后剩余的最小体积。
考虑逐个物品添加。
最开始什么也没装,所以容量有多少剩多少。
每一轮添加物品至容量为
i
i
i 的背包都有两种选择——装入该物品和不装入该物品,取二者剩余体积的最小值即可。
- 若装入该物品(假定其体积为 v v v ),我们可以事先装入该物品,问题就变成了寻找容量为 i − v i-v i−v 的背包剩余的最小体积。
- 若不装入,保持 d p i dp_i dpi 不变即可。
- 综上 , d p i = m i n { d p i − v , d p i } dp_i=min\{dp_{i-v}, dp_i\} dpi=min{dpi−v,dpi} (注意这里的 d p i − v dp_{i-v} dpi−v 必须是上一轮的结果,如果用该轮结果,物品可能被添加两次)。
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e4+4;
int dp[N]; // dp[i]容量为i的背包剩余的最小体积
int main()
{
int V;
int n;
scanf("%d%d", &V, &n);
for (int i = 1; i <= V; ++i) dp[i] = i; // 最开始有多少剩多少
while (n--) {
int x;
scanf("%d", &x);
// 为保证dp[j-x]总是上一轮结果,采用倒序遍历
for (int j = V; j >= x; --j) {
// min{不放物品x则剩余容量不变,放置物品x剩余容量取决于上一轮j-x空间的情况}
dp[j] = min(dp[j], dp[j-x]);
}
}
printf("%d", dp[V]);
return 0;
}