动态规划
1049.最后一块重量的石头II
解题思路: 抽象思维, 将石头分成两堆质量最接近的, 则一定是碰撞后质量最小的
- 对质量求和, 取一半的值
- 对石头进行01背包处理, 让其尽可能地接近背包容量最大值
- 01背包处理后, 一半是sum-dp[bagSize], 另外一半是 dp[bagSize]
- 取两个的差值即可
代码:
class Solution {
public int lastStoneWeightII(int[] stones) {
// 分割成两堆, 一堆最接近一半值, 则两堆碰撞后剩下的重量最小
int sum = Arrays.stream(stones).sum();
int bagSize = sum >> 1;
int[] dp = new int[bagSize + 1];
for(int i=0; i<stones.length; i++){
for(int j=bagSize; j>=stones[i]; j--){
dp[j] = Math.max(dp[j], dp[j-stones[i]] + stones[i]);
}
}
return sum - 2 * dp[bagSize];
}
}
494.目标和
解题思路:
- 回溯, 对于每个元素有两种情况, 取其正数或者其负数
- 本题需要用到所有元素, 所以必须在叶子节点判断 sum == target?
- 注意可能有0的存在, 所以最后一个节点取正数和取负数都得分别进行判断和记录
- 时间复杂度为O(2^n)
代码:
class Solution {
int ans = 0;
public int findTargetSumWays(int[] nums, int target) {
// 回溯法试试 --- 能过, 但是时间复杂度有点高 --- 改成动态规划
backtracking(nums, target, 0, 0);
return ans;
}
public void backtracking(int[] nums, int target, int index, int sum) {
// 递归出口 --- 只收集叶子节点的值
if(index == nums.length - 1){
if(sum + nums[index] == target) ans++;
if(sum - nums[index] == target) ans++;
return;
}
backtracking(nums, target, index + 1, sum + nums[index]);
backtracking(nums, target, index + 1, sum - nums[index]);
}
}
474.一和零
解题思路:
- 01背包问题, 只是背包有两个维度, 一个维度是0的数量, 一个维度是1的数量
- 先统计每个字符中0和1的数量
- 然后遍历物品, 再倒序遍历背包, 对于两个维度的背包来说需要双重for循环进行判断
- 背包的价值就是当前的个数 1
代码:
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
// 记录每个字符串中的0和1的个数
int size = strs.length;
int[][] arr = new int[size][2];
for(int i=0; i < size; i++) {
for(int j=0; j < strs[i].length(); j++) {
if(strs[i].charAt(j) == '0'){
arr[i][0]++;
}else arr[i][1]++;
}
}
// 动态规划 --- 背包的容量: m个0, n个1
int[][] dp = new int[m + 1][n + 1];
// 遍历物品
for(int i=0; i < size; i++){
// 遍历背包 --- 两个维度的背包, 需要双重for循环遍历
for(int j=m; j >= arr[i][0]; j--){
for(int k=n; k >= arr[i][1]; k--){
// 价值就是其个数1
dp[j][k] = Math.max(dp[j][k], dp[j-arr[i][0]][k-arr[i][1]] + 1);
}
}
}
return dp[m][n];
}
}