代码随想录-2023/08/09

107 阅读2分钟

动态规划

1049.最后一块重量的石头II

解题思路: 抽象思维, 将石头分成两堆质量最接近的, 则一定是碰撞后质量最小的

  1. 对质量求和, 取一半的值
  2. 对石头进行01背包处理, 让其尽可能地接近背包容量最大值
  3. 01背包处理后, 一半是sum-dp[bagSize], 另外一半是 dp[bagSize]
  4. 取两个的差值即可

代码:

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.目标和

解题思路:

  1. 回溯, 对于每个元素有两种情况, 取其正数或者其负数
  2. 本题需要用到所有元素, 所以必须在叶子节点判断 sum == target?
  3. 注意可能有0的存在, 所以最后一个节点取正数和取负数都得分别进行判断和记录
  4. 时间复杂度为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.一和零

解题思路:

  1. 01背包问题, 只是背包有两个维度, 一个维度是0的数量, 一个维度是1的数量
  2. 先统计每个字符中0和1的数量
  3. 然后遍历物品, 再倒序遍历背包, 对于两个维度的背包来说需要双重for循环进行判断
  4. 背包的价值就是当前的个数 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];
    }
}