代码随想录刷题day29

1、不同的二叉搜索树

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

感悟:通过举例推导,n=1,只有一颗二叉搜索树;n=2,有两颗;n=3,有5颗,且可以看出,是在n=2的前提上演变过来的。得出递归公式:dpi += dp以j为头结点左子树节点数量 * dp以j为头结点右子树节点数量,即:dpi += dpj - 1 * dpi - j;

java 复制代码
class Solution {
    public int numTrees(int n) {
        int[] dp = new int[n+1];
        dp[0] = 1;
        //dp[1] = 1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=i;j++){
                dp[i] += dp[j-1]*dp[i-j];
            }
        }
        return dp[n];
    }
}

2、分割等和子集

给你一个 只包含正整数非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

感悟:典型的0-1背包问题,数组元素的和是固定的,所以背包容量是sum/2,看是否有能刚好装满背包的元素。0-1背包问题的递推公式:dpj = max(dpj, dpj - weight\[i] + valuei);当dptarget=target时,表示找到了能装满背包的元素。

注意:动态数组使用一维数组时,第二层循环需要倒序,不然可能会把某个元素放入多次;

java 复制代码
class Solution {
    public boolean canPartition(int[] nums) {
        int target = 0;
        for(int n :nums){
            target += n;
        }
        if(target % 2 >0) return false;
        int m = target/2;
        int[] dp = new int[m+1];
        for(int i=0;i<nums.length-1;i++){
            for(int j=m;j>0 && j>=nums[i];j--){
                dp[j] = Math.max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }
        return dp[m] == m;
    }
}

3、最后一块石头的重量II

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

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

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

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

感悟:和上题类似,当两块石头尽可能接近时,可以获得最小的重量。

java 复制代码
class Solution {
    public int lastStoneWeightII(int[] stones) {
        if(stones == null || stones.length == 0) return 0;
        int len = stones.length;
        int sum = 0;
        for(int n : stones){
            sum += n;
        }
        int m = sum/2;
        int[] dp = new int[m+1];
        for(int i=0;i<len;i++){
            for(int j=m;j>=stones[i];j--){
                dp[j] = Math.max(dp[j],dp[j-stones[i]]+stones[i]);
            }
        }
        return sum-2*dp[m];
    }
}

4、目标和

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

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

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

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

感悟:整个数组分成left、right两部分,left+right=sum,left-right=target,所以left = (sum+target)/2,又转化为0-1背包问题了。用二维数组时,能更清晰的推导出转移方程。

归纳总结如下:

  • 不放物品i:即背包容量为j,里面不放物品i,装满有dpi - 1j中方法。

  • 放物品i: 即:先空出物品i的容量,背包容量为(j - 物品i容量),放满背包有 dpi - 1j - 物品i容量 种方法。

本题中,物品i的容量是numsi,价值也是numsi

递推公式:dpij = dpi - 1j + dpi - 1j - nums\[i];

java 复制代码
class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        int len = nums.length;
        for(int n : nums){
            sum += n;
        }
        if((sum+target)%2 == 1) return 0;
        if((sum+target) < 0) return 0;
        int left = (sum+target)/2;
        int[] dp = new int[left+1];
        dp[0] = 1;
        for(int i=0;i<len;i++){
            for(int j=left;j>=nums[i];j--){
                dp[j] += dp[j-nums[i]];
            }
        }
        return dp[left];
    }
}

5、一和零

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

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

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

感悟:本题稍有点特殊,背包有两个维度,0和1分别是一个维度。递推公式:dpij = max(dpij, dpi - zeroNumj - oneNum + 1);

java 复制代码
class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        if(strs.length == 0) return 0;
        int[][] dp = new int[m+1][n+1];
        for(String s:strs){
            int zeroNum = 0,oneNum = 0;
            for(char c:s.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);
                }
            }
        }
        return dp[m][n];
    }
}

总结:0-1背包问题,当递推公式不好直接看出来时,可以先尝试使用二维数组去推理。

0-1背包的递推公式推理过程抽象化如下:

  • 不放物品i:背包容量为j,里面不放物品i的最大价值是dpi - 1j

  • 放物品i:背包空出物品i的容量后,背包容量为j - weighti,dpi - 1j - weight\[i] 为背包容量为j - weighti且不放物品i的最大价值,那么dpi - 1j - weight\[i] + valuei (物品i的价值),就是背包放物品i得到的最大价值

递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

相关推荐
GreenTea6 小时前
深度解读 Anthropic 多智能体报告:更强的模型 ≠ 更好的协调
前端·后端·算法
fqq37 小时前
力扣刷题前置Java语法
算法·leetcode·职场和发展
半夏微凉半夏殇10 小时前
x11与weston对比,优先选哪个
leetcode·均值算法·eclipse
2501_9065651210 小时前
数学之美探究
算法
oier_Asad.Chen10 小时前
【洛谷题解/AcWing题解/OI学习笔记】洛谷P2868【USACO07DEC】Sightseeing Cows G(01分数规划求最大比率)
算法·图论·spfa·二分·负环
leisoo809712 小时前
财报数据怎么排雷本地化Python构建财务异常预警系统
人工智能·python·算法
luj_176814 小时前
塔防牌:策略与卡牌的智慧碰撞
服务器·c语言·开发语言·经验分享·算法
郝学胜-神的一滴14 小时前
干货版《算法导论》17:二叉树核心原理、遍历逻辑与高阶实操全解
数据结构·c++·python·算法·计算机·编程
爱编程的小新☆15 小时前
【LeetCode】从递归到 Flood Fill:5 道题吃透 DFS 的选择、回溯与标记
java·算法·leetcode·深度优先·回溯·flood fill
Water_Sunzhipeng15 小时前
2024牛客暑期多校训练营1
算法