一句话总结:甚至都想不到怎么套背包问题。
原题链接:1049 最后一块石头的重量II
这题和昨天的分割等和子集很像,但是需要想到这一层才行。
这里首先将target设置为sum / 2,dp数组的计算过程与前一题是一样的。最后需要返回的是sum与2 * dp[target]的差值。
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = 0;
for (int x : stones) sum += x;
int target = sum / 2;
int[] dp = new int[target + 1];
for (int i = 0; i < stones.length; ++i) {
for (int j = target; j >= stones[i]; --j) {
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
return sum - 2 * dp[target];
}
}
原题链接: 494 目标和
最难的是将这题转换为背包问题。
假设加法的总和为x,那么减法对应的总和就是sum - x。而要求的是 x - (sum - x) = target。
因此x = (target + sum) / 2。此时问题就转化为,装满容量为x的背包,有几种方法。那么这里的x,就是背包问题的背包容量。
同时要注意到(target + sum) / 2 计算的过程中向下取整有没有影响。比如(sum + target)% 2 == 1时即是无解的。同时target的绝对值大于sum时也是无解的。
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for (int x : nums) sum += x;
if (Math.abs(target) > sum) return 0;
if ((target + sum) % 2 == 1) return 0;
int obj = (target + sum) / 2;
int[] dp = new int[obj + 1];
dp[0] = 1;
for (int i = 0; i < nums.length; ++i) {
for (int j = obj; j >= nums[i]; --j) {
dp[j] += dp[j - nums[i]];
}
}
return dp[obj];
}
}
原题链接:474 一和零
此题也是一个01背包问题,但是物品的重量有两个维度,分别是某一字符串里的zeroNum和oneNum。由此确定递推公式。
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int [][]dp = new int[m + 1][n + 1];
for (String str : strs) {
int oneNum = 0;
int zeroNum = 0;
for (char c : str.toCharArray()) {
if (c == '0') zeroNum++;
else oneNum++;
}
for (int i = m; i >= zeroNum; i--) {
for (int j = n; j >= oneNum; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
}