算法修炼Day43|1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

100 阅读2分钟

LeetCode:1049. 最后一块石头的重量 II - 力扣(LeetCode)

1.思路

尝试将物品拆分成尽可能相近的两部分,两者相减才能得出剩余的最小值。具体操作:求和/2得出背包容量值,背包容量下所能盛放的最大重量由不放当前石头和放当前石头决定。最终结果是总和减去两倍(选出与被撞)的当前容量的值。

2.代码实现
class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = 0;
        for (int i : stones) {
            sum += i;
        }
        // int target = sum >> 1;
        int target = sum / 2;
        // 初始化dp[]数组:dp[target]表示容量在target时所能盛放的最大值
        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]);
            }
        }
        return sum - 2 * dp[target];
    }
}
3.复杂度分析

时间复杂度:O(N * M).

空间复杂度:O(N).

LeetCode:494. 目标和 - 力扣(LeetCode)

1.思路

为所有数字添加+-号得出target,则需要判断两块数字的和为sum下两块数字的值分别为size和right(size + right = sum; size - right = target),对物品进行遍历判断和为size存在的个数即可。

2.代码实现
class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        if (target > 0 && target > sum) {
            return 0;
        }
        if (target < 0 && sum < -target) {
            return 0;
        }
        // 设目标为target时,正数为left,负数为right。left + right = sum; left - right = target;
        // size 为正数的值
        int size = (sum + target) / 2;
        if (size < 0) {
            size = - size;
        }
        int[] dp = new int[size + 1];
        dp[0] = 1;
        for (int i = 0; i < nums.length; i++) {
            for (int j = size; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[size];
    }
}
3.复杂度分析

时间复杂度:O(N * M).

空间复杂度:O(N).

LeetCode:

1.思路

判断条件从一个变为两个,则dp[][]声明为二维的,对相应的0和1共同进行判断。遍历每个字符串,对字符串内元素个数进行判断,再将其放入背包,取其最大值即可。

2.代码实现
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 (int i = 0; i < strs.length; i++) { // 物品
            // 当前字符串中所具有的 0 和 1 的个数
            int oneNum = 0;
            int zeroNum = 0;
            char[] ch = strs[i].toCharArray();
            for (int j = 0; j < ch.length; j++) {
                if (ch[j] == '0') {
                    zeroNum++;
                } else {
                    oneNum++;
                }
            }
            // 背包倒序遍历,避免重复内容加入
            for (int k = m; k >= zeroNum; k--) {
                for (int l = n; l >= oneNum; l--) {
                    dp[k][l] = Math.max(dp[k][l], dp[k - zeroNum][l - oneNum] + 1);
                }
            }
        }
        return dp[m][n];
    }
}
3.复杂度分析

时间复杂度:O(N * m * n).

空间复杂度:O(N).