代码随想录算法训练营第43天
今日任务
● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零
链接:https://docs.qq.com/doc/DUEVMRHZOemN5S2Vm (opens in a new tab)
1049. 最后一块石头的重量 II
自己想法
将石头分为尽可能重量相等的两堆,可以使得两堆石头的重量差最小。
因为分开两堆后,不管怎么粉碎,最后一块石头的重量都是两堆石头的重量差。
所以问题转化为:将数组内的数看作物品,其价值和重量都是这个数,
将数组和的一半(向下取整)看作背包的容量,转化为01背包问题。
最终返回数组和减去两倍背包问题的最大值。
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = Arrays.stream(stones).sum();
int nearHalf = sum / 2;
int[] dp = new int[nearHalf + 1];
for (int i = 0; i < stones.length; i++) {
for (int j = nearHalf; j >= stones[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
return sum - 2 * dp[nearHalf];
}
}494. 目标和
题解想法
将数组分为两部分,一部分是加号的数,一部分是减号的数。
本题需要将背包装满,在求装满背包有几种方法的情况下,递推公式一般为:dp[j] += dp[j - nums[i]];
dp[j]表示容量为j的背包有多少种方法装满。外层循环表示遍历物品,内层j循环表示遍历背包容量。
class Solution {
public int findTargetSumWays(int[] nums, int target) {
// bag - (sum - bag) = target
// -> bag = (target + sum) / 2
// -> bag必须为整数; bag必须大于等于0
int twin_bag = Arrays.stream(nums).sum() + target;
if (twin_bag % 2 != 0) return 0;
int bag = twin_bag / 2;
if (bag < 0) return 0;
int[] dp = new int[bag + 1];
dp[0] = 1;
for (int i = 0; i < nums.length; i++) {
for (int j = bag; j >= nums[i]; j--) {
dp[j] += dp[j - nums[i]];
}
}
return dp[bag];
}
}474.一和零
题解想法
本题是一个多维费用的01背包问题。
dp[i][j]最多有i个0和j个1的strs的最大子集的大小。外层循环表示遍历物品,内层i和j循环表示遍历背包容量。
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m + 1][n + 1];
for (String str : strs) {
int countZero = 0;
int countOne = 0;
for (char c : str.toCharArray()) {
if (c == '0') {
countZero++;
} else if (c == '1') {
countOne++;
}
}
for (int i = m; i >= countZero; i--) {
for (int j = n; j >=countOne ; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - countZero][j - countOne] + 1);
}
}
}
return dp[m][n];
}
}