一、0-1背包理论基础
题目描述:给你一个可装载重量为 W
的背包和 N
个物品,每个物品有重量和价值两个属性。其中第 i
个物品的重量为 wt[i]
,价值为 val[i]
。现在让你用这个背包装物品,每个物品只能用一次,在不超过被包容量的前提下,最多能装的价值是多少?
物品不能分割,要么装进包里,要么不装,不能切成两半,这是0-1背包的来历
动态规划标准套路
-
明确两点,状态和选择
0-1背包状态有两个,背包的容量和可选择的物品
-
明确dp数组的定义
dp(i)(w):对应前i个物品,当前容量为w,这种情况下能装的最大价值是dp(i)(w)
为什么这么记?套路
-
明确base case
dp(0)(…) = dp(…)(0) = 0
-
思考状态转移的逻辑
-
翻译代码,处理边界情况
二、分割等和子集
boolean canPartition(int[] nums) {
int sum = 0;
for (int num : nums) sum += num;
// 和为奇数时,不可能划分成两个和相等的集合
if (sum % 2 != 0) return false;
int n = nums.length;
sum = sum / 2;
boolean[][] dp = new boolean[n + 1][sum + 1];
// base case
for (int i = 0; i <= n; i++)
dp[i][0] = true;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= sum; j++) {
if (j - nums[i - 1] < 0) {
// 背包容量不足,不能装入第 i 个物品
dp[i][j] = dp[i - 1][j];
} else {
// 装入或不装入背包
//注意 || 的使用非常巧妙,只要有一种情况为true dp[i][j]就为true
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
}
}
}
return dp[n][sum];
}