1049. 最后一块石头的重量 II
可以把石头大体分成两堆,如果两堆的重量越接近,互相粉碎后剩下的石头重量就越小。那么问题就转化成了找到数组和的一半,或者是最接近这个一半的数字。
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = 0;
for(int stone : stones){
sum += stone;
}
int target = sum / 2;
// dp[j]: 容量为j的背包最多装下的重量为dp[j]
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]);
}
}
// 最后分成两堆石头互相粉碎,一堆是dp[target],另一堆是sum - dp[target]
return (sum - dp[target]) - dp[target];
}
}
494. 目标和
class Solution {
public int findTargetSumWays(int[] nums, int target) {
/**
left + right = sum
left - right = target
合并得: left = (sum + target) / 2
问题转化为在nums中求得何为left有几种组合方法
*/
int sum = 0;
for(int num : nums){
sum += num;
}
int left = (sum + target) / 2;
if((sum + target) % 2 == 1)return 0; // 此时是无解的
if(Math.abs(target) > sum)return 0; // 此时也是无解的
// dp[j]: 装满容量为j的背包,有几种方法
int[] dp = new int[left + 1];
// 初始化
dp[0] = 1;
for(int i = 0; i < nums.length; i++){
for(int j = left; j >= nums[i]; j--){
// 求组合类问题的公式,都是类似这种:dp[j] += dp[j - nums[i]]
dp[j] += dp[j - nums[i]];
}
}
return dp[left];
}
}
474. 一和零
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
// dp[i][j]: 最多有 i个0 和 j个1 的最大子集的长度
int[][] dp = new int[m + 1][n + 1]; // 默认初始化为0
for(String str : strs){ // 遍历物品
int zeroNum = 0, oneNum = 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); // 分别是不放入当前str和放入当前str
}
}
}
return dp[m][n];
}
}