代码随想录算法训练营 day 43: ● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

56 阅读2分钟

1049. Last Stone Weight II

思路是把石头分两堆,两堆的重量尽量接近,即接近于 SUM / 2。 那么是01背包问题,取重量接近于SUM/2的子集。

class Solution {
    public int lastStoneWeightII(int[] stones) {
        if(stones.length == 1) {
            return stones[0];
        }

        int sum = 0;

        for(int i : stones) {
            sum += i;
        }

        int[] dp = new int[15001];
        dp[0] = 0;
        int target = 0;

        target = sum/2;


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

        return sum - dp[target] - dp[target];
    }
}

494. Target Sum

把原数组分成两堆left和right,则有: left + right = sum left - right = target 可得: left = (sum+target) / 2 整数向下取整,那么(sum+target) % 2不为0时,无解。 target可为负,那么 abs(sum) < abs(target)时,无解。 可以只考虑正数,target为负时,取其绝对值。

以上,可以化为01背包问题。求和为(sum+target)/2的子集数目。 注意不是求最大子集和,而是求子集数。 递推公式为:dp[j] = dp[j] + dp[j-nums[i]]

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;

        for(int i:nums) {
            sum += i;
        }

        if(target < 0 && sum < -target) return 0;
        if((target + sum) % 2 != 0) return 0;

        int tar = (target + sum) / 2;

        if(tar<0) tar = -tar;

        int[] dp = new int[tar+1];
        dp[0] = 1;

        for(int i=0; i<nums.length; i++) {
            for(int j=tar; j>=nums[i]; j--) {
                dp[j] += dp[j-nums[i]];
            }
        }

        return dp[tar];
    }
}

474. Ones and Zeroes

  1. 确定dp数组(dp table)以及下标的含义

dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]

  1. 确定递推公式

dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。

dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。

然后我们在遍历的过程中,取dp[i][j]的最大值。

所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);

此时大家可以回想一下01背包的递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

对比一下就会发现,字符串的zeroNum和oneNum相当于物品的重量(weight[i]),字符串本身的个数相当于物品的价值(value[i])。

这就是一个典型的01背包!  只不过物品的重量有了两个维度而已。

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][] dp = new int[m+1][n+1];

        int oneNum = 0, zeroNum = 0;

        for(String str : strs) {
            oneNum = 0;
            zeroNum = 0;

            for(char c : str.toCharArray()) {
                if(c == '0') {
                    zeroNum++;
                }
                if(c == '1') {
                    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);
                }
            }

        }

        return dp[m][n];
    }
}