hot100动态规划刷题

01背包:

416. 分割等和子集 - 力扣(LeetCode)

二维更容易理解

dp 代表从0,i索引是否能组成target,01背包

初始化:0行0列,对于0列都为true,因为target=0,默认不选

对于0行,拿到nums0恰好填满时候为true。所以dp0nums\[0]=true,如果没有越界

递推公式:对于当前numsi可以选择拿或者不拿,取到一个或的结果。

复制代码
class Solution {
    public boolean canPartition(int[] nums) {
        // 类似01背包
        int sum = 0;
        for(int i = 0; i < nums.length; i++){
            sum += nums[i];
        }
        if(sum % 2 != 0) return false;
        int target = sum /2;
        // 从0,i上能否填满target
        boolean dp[][] = new boolean[nums.length][target+1];
        // dp[i][j] = dp[i-1][j] || dp[i-1][target-nums[i]]
        // 当target为0的时候,都不选默认为true
        for(int i = 0; i < nums.length; i++){
            dp[i][0] = true;
        }
        // 当i=0的时候,如果恰好填满target为true
        if(nums[0]<= target) dp[0][nums[0]]=true;
        for(int i = 1; i < nums.length;i++){
            for(int j = 1; j <= target; j++){
                dp[i][j] = dp[i-1][j];
                if(j-nums[i] >= 0){
                    dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]];
                }
            }
        }
        return dp[nums.length-1][target];
    }
}

完全背包

322. 零钱兑换 - 力扣(LeetCode)

完全背包,先遍历物品,内部遍历容量,初始化的最大值注意,按照1的最多数量+1就可以

出错的点:容量错误,用整数最大值会越界

复制代码
class Solution {
    public int coinChange(int[] coins, int amount) {
        int dp[] = new int[amount+1];
        for(int i = 1; i <= amount; i++){
            dp[i] = amount+1;
        }
        for(int i = 0; i < coins.length;i++){
            for(int j = 1; j <= amount;j++){
                if(j - coins[i] >=0){
                    dp[j] = Math.min(dp[j], dp[j-coins[i]]+1);
                }
            }
        }
        if(dp[amount] == amount+1) return -1;
        return dp[amount];
    }
}

279. 完全平方数 - 力扣(LeetCode)

完全背包的变种:和零钱兑换几乎一样,先遍历物品,此时物品是i*i,平方数,内部正序循环容量,初始化都用容量+1.

复制代码
class Solution {
    public int numSquares(int n) {
        int dp [] = new int[n+1];
        dp[0]=0;
        for(int i=1; i<=n;i++){
            dp[i]=n+1;
        }
        for(int i = 1; i * i <= n;i++){
            int sq = i * i;
            for(int j = 1; j <= n;j++){
                if(j >= sq){
                    dp[j] = Math.min(dp[j], dp[j - sq] + 1);
                }
            }
        }
        return dp[n];
    }
}

139. 单词拆分 - 力扣(LeetCode)

类似完全背包:但是不能套用外层遍历物品的模板,字符串位置问题一般是先遍历容量

推理定义:前i个字符能否拆分,要看前j个能否拆分+j,i是否是一个单词

初始化:前0个为true。

公式:要算 dp[i],就把所有 j < i 试一遍,看 dp[j] + s[j..i-1] 是否成立。只要有一个成立,dp[i] 就为 true。

算 dp[i] 时,前面所有 dp[0..i-1] 都已经算好,直接查表复用。不是只复用 dp[i-1]。

复制代码
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        // 从前i个字符串能否被worddict填满。
        // dp[i] = dp[j] && s[j, i];
        boolean dp[] = new boolean[s.length()+1];
        dp[0]=true;
        //在前i个字符每一种i的情况下,只要有一种j可以成功切分,dp[i]就是true,所以找到了就break
        // 内层循环是为了尝试所有切分点,让i可以拆分为j+(j,i)
        for(int i = 1; i <= s.length();i++){
            for(int j = 0; j <= i;j++){
                if(dp[j] && (wordDict.contains(s.substring(j,i)))){
                    dp[i]=true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

连续子数组/子串"问题

152. 乘积最大子数组 - 力扣(LeetCode)

maxdpi以i结尾的最大值

mindpi以i结尾的最小值

复制代码
class Solution {
    public int maxProduct(int[] nums) {
        // dpmax[i] = dpmin[i-1] * nums[i] if nums[i] < 0
        // dpmax[i] = dpmax[i-1] * nums[i] if nums[i] > 0
        int maxdp [] = new int[nums.length];
        int mindp [] = new int[nums.length];
        maxdp[0] = nums[0];
        mindp[0]=nums[0];
        int result =nums[0];
        for(int i = 1; i < nums.length;i++){
            maxdp[i] = Math.max(nums[i], Math.max(nums[i] * mindp[i-1], nums[i] * maxdp[i-1]));
            mindp[i] = Math.min(nums[i], Math.min(nums[i] * maxdp[i-1], nums[i] * mindp[i-1]));
            result = Math.max(result, maxdp[i]);
        }
        return result;
    }

}

32. 最长有效括号 - 力扣(LeetCode)

推理方程:dp[i] = 以 s[i] 结尾的最长有效括号子串长度。

初始化:dp0 = 0,因为一个字符形成不了,其余也为0

状态转移:s[i]=='(' 时为 0;s[i]==')' 时,若前一个是 ( 则 dp[i]=dp[i-2]+2,否则跳过 dp[i-1] 看 s[j](j=i-dp[i-1]-1)能否配对。答案取 max(dp),全程用 res 记录。

复制代码
class Solution {
    public int longestValidParentheses(String s) {
        if(s.length()==0)return 0;
        // 以i结尾的字符换的最长连续子串
        int dp[]=new int[s.length()];
        dp[0]=0; // 初始化
        int res = 0;
        for(int i = 1; i < s.length();i++){
            // i位置以(结尾必然无法拼接
            if(s.charAt(i) == '('){
                dp[i]=0;
                continue;
            }
            if(s.charAt(i) == ')'){
                if(s.charAt(i-1)=='('){
                    if(i-2<0) {
                        dp[i]=2;
                    }else{
                        dp[i] = dp[i-2]+2;
                    }
                }else{
                    int j = i-dp[i-1] -1;
                    if(j-1>= 0 && s.charAt(j) == '('){
                        dp[i] = dp[i-1]+2+dp[j-1];
                    }else if(j>= 0 && j<1 &&s.charAt(j) == '('){
                        dp[i] = dp[i-1]+2;
                    }
                }
                if(dp[i] > res){
                    res = dp[i];
                }
            }
        }
        return res;
    }
}

300. 最长递增子序列 - 力扣(LeetCode)

错误分析:初始化没初始化全部数组

推理方程:以i位置结束的最大递增长度

初始化:所有dp位置初始为1.因为至少一个

转移:对于每个i,看他前面的所有j位置,如果numsj < numsi,找到一种dpi的可能dpj+1,然后取dpi的最大值。

复制代码
class Solution {
    public int lengthOfLIS(int[] nums) {
        // i位置结尾的最大递增子序列是dp[i]
        int dp[] = new int[nums.length];
        Arrays.fill(dp,1);
        int res=dp[0];
        for(int i = 1;i<nums.length;i++){
            for(int j = 0; j<i; j++){
                if(nums[i] > nums[j]){
                    dp[i]=Math.max(dp[i], dp[j]+1);
                }
            }
            res=Math.max(dp[i],res);
        } 
        return res;
    }
}

总结:

01背包:

简化为一维数组之后,外循环物品,内循环容量逆序

完全背包:

简化为一维数组之后,外循环物品,内循环容量正序

连续子串/子数组 ,或者扩展时需要看末尾元素的:

用"以 i 结尾"

前缀的整体性质,扩展时不关心末尾具体是谁的:

用"前 i 个"

相关推荐
weixin_531094692 小时前
heap_4内存管理
java·开发语言·算法
余额瞒着我当琳2 小时前
红黑树的实现--红黑树的概念、规则、效率、结构、旋转、查找、验证
数据结构·c++·算法
程序员南飞2 小时前
win系统安装minio
java·数据结构·算法
青少儿编程课堂2 小时前
AC自动机多模式串匹配精讲:校园关键词巡检实战解析
c++·python·算法·bfs·信息学竞赛
longlongzihan3 小时前
LeetCode 5. 最长回文子串:从暴力匹配到中心扩展的思维演进
c++·算法·leetcode
waoooqwe3 小时前
问卷样本真实吗
大数据·数据库·算法·数据分析
苦瓜打怪兽3 小时前
leetcode 热题100(11. 盛最多水的容器)
python·算法·leetcode
w_zero_one4 小时前
链表(4)
java·数据结构·算法
吠品4 小时前
GitHub 周榜拆解:决策小模型扎堆上位
c语言·开发语言·算法