算法训练营第四十三天|1049. 最后一块石头的重量 II、494. 目标和、474.一和零

59 阅读2分钟

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];
    }
}