第四十三天 | 动态规划 part05

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

题目链接/文字讲解:最后一块石头的重量II

视频讲解:https://www.bilibili.com/video/BV14M411C7oV

题设:有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 xy,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

思路:将石头分为2组,其和尽量相近即可。动规五部曲:

1.dp数组含义:dp[i][j]代表前i块石头最接近j的重量和(重量和小于等于j)。

2.递归表达式:dp[i][j]等于dp[i-1][j]dp[i-1][j-stone[i]]+stone[i]中的最大值

3.初始化:dp[0][stone[0]到target]初始为stone[0]。

4.递归顺序:先物品再背包,从前往后。

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = Arrays.stream(stones).sum();
        int target = sum / 2;
        int[][] dp = new int[stones.length][target + 1];
        for (int i = stones[0]; i <= target; i++) dp[0][i] = stones[0];
        for (int i = 1; i < stones.length; i++) {
            for (int j = 1; j <= target; j++) {
                if (j >= stones[i]){
                    dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-stones[i]]+stones[i]);
                }else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        return (sum-2*dp[stones.length-1][target]);
    }
}
class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = Arrays.stream(stones).sum();
        int target = sum >> 1;
        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];
    }
}

494.目标和

题目链接/文字讲解:目标和

视频讲解:https://www.bilibili.com/video/BV1o8411j73x

题设:给你一个非负整数数组 nums 和一个整数 target

向数组中的每个整数前添加 '+''-' ,然后串联起所有整数,可以构造一个 表达式

  • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1"

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

思路:x = (target + sum) / 2,装满容量为x的背包,有几种方法。两种无解情况:target+sum为奇数,target比sum大。动规五部曲:

1.dp数组含义:dp[j]代表装满容量为j的包,有多少种方法。

2.确定递推公式-只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。

dp[j] += dp[j - nums[i]]。解决组合问题的通用公式。

3.dp数组如何初始化-从递推公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0。

4.遍历顺序:nums放在外循环,target在内循环,且内循环倒序。

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = Arrays.stream(nums).sum();
        if ((sum + target) % 2 == 1) return 0;
        if (target > sum) return 0;
        if (target < 0 && sum < -target) return 0;
        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];
    }
}

474.一和零

题目链接/文字讲解:一和零

视频讲解:https://www.bilibili.com/video/BV1rW4y1x7ZQ

题设:给你一个二进制字符串数组 strs 和两个整数 mn

请你找出并返回 strs 的最大子集的长度,该子集中 最多m0n1

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y子集

思路:动规五部曲:

1.dp数组含义:dp[i][j]:最多有i个0和j个1的最大子集的大小为dp[i][j]

2.确定递推公式:

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

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

3.dp数组如何初始化,全部初始为0。

4.确定遍历顺序:外物品,里背包,从后往前。

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][] dp = new int[m + 1][n + 1];
        int oneNum, zeroNum;
        for (String str : strs) {
            oneNum=0;
            zeroNum=0;
            for (char c : str.toCharArray()) {
                if (c=='1')oneNum++;
                if (c=='0')zeroNum++;
            }
            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];
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容