题目
动态规划-01背包问题
思路
先说一下01背包问题:
有 N N N 件物品和一个容量为 V V V 的背包。放入第 i i i 件物品耗费的空间是 C i C_i Ci,得到的价值是 W i W_i Wi,求解最大价值总和。
这类题是最基本的背包问题,每个物品只有拿或不拿两种选择(这也是被称为“01”的原因,0表示不拿,1表示拿)。其它的背包问题及讲解可以参考《背包九讲》。01背包问题的思路为:
设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示总空间为 j j j 的背包装前 i i i 件物品的最大价值,则答案为 d p [ N ] [ V ] dp[N][V] dp[N][V]。状态转移方程为 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − C [ i ] ] + W [ i ] ) dp[i][j]=max(dp[i-1][j],dp[i-1][j-C[i]]+W[i]) dp[i][j]=max(dp[i−1][j],dp[i−1][j−C[i]]+W[i]),也就是最大价值取“拿第 i i i 件物品和不拿第 i i i 件物品”的最大值。拿第 i i i 件物品用式子 d p [ i − 1 ] [ j − C [ i ] ] + W [ i ] dp[i-1][j-C[i]]+W[i] dp[i−1][j−C[i]]+W[i] 表示,即消耗 C [ i ] C[i] C[i] 的空间换取 W [ i ] W[i] W[i] 的价值。不拿第 i i i 件物品用式子 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j] 表示,即和上一件物品的状态一样。
这道题是01背包的变形,要使剩余空间最小,那么使总物品的空间最大即可。代码中使用的是空间优化版本,关于空间优化,因为上面的状态转移方程中当前状态 d p [ i ] [ . . . ] dp[i][...] dp[i][...] 只与上一状态 d p [ i − 1 ] [ . . . ] dp[i-1][...] dp[i−1][...] 有关,所以二维 d p dp dp 数组可以优化为只需要一维。然后滚动使用即可,具体见代码。
代码
#include <stdio.h>
#define N 33
#define M 20005
int max(int a, int b) { return a > b ? a : b; }
int main(void) {
int n = 0, v = 0, a[N], dp[M];
scanf("%d%d", &v, &n);
int i = 0, j = 0;
for (i = 0; i < n; i++) {
scanf("%d", a + i);
}
// dp 数组初始化为0
for (i = 0; i <= v; i++) {
dp[i] = 0;
}
for (i = 1; i <= n; i++) {
// 思考一下为什么要从右往左更新
for (j = v; j >= a[i - 1]; j--) {
dp[j] = max(dp[j], dp[j - a[i - 1]] + a[i - 1]);
}
}
// 剩余最小
printf("%d\n", v - dp[v]);
return 0;
}