当前位置: 首页 > article >正文

DP动态规划+贪心题目汇总

文章目录

  • 背包
    • 01背包
      • 416. 分割等和子集
    • 完全背包
      • 279. 完全平方数
      • 322. 零钱兑换
  • 两个字符串DP
    • LCR 095. 最长公共子序列
    • 139. 单词拆分
  • 单个数组字符串DP
    • 5. 最长回文子串
    • 300. 最长递增子序列
    • 53.最大子数组和
    • 152. 乘积最大子数组
    • 198. 打家劫舍
  • 三角形
    • 120. 三角形最小路径和
  • 贪心
    • 121. 买卖股票的最佳时机
    • 55. 跳跃游戏
    • 45. 跳跃游戏 II
    • 区间问题
      • 763. 划分字母区间
      • 56. 合并区间

背包

01背包

[图片]

416. 分割等和子集

//f[i][j]表示能否从 nums中前i个 选出一个和恰好等于 j 的子序列.这不就是01背包
f[i][j] = f[i-1][j - nums[i]] || f[i-1][j] 选和不选
01背包优化成一维形式,i维度不要,内层循环从target递减
也可以用记忆化搜索做

class Solution {
    public boolean canPartition(int[] nums) {
        //f[i][j]表示能否从 nums中前i个 选出一个和恰好等于 j 的子序列.这不就是01背包
        //f[i][j] = f[i-1][j - nums[i]] || f[i-1][j] 选和不选
        //01背包优化成一维形式,i维度不要,内层循环从target递减
        int n = nums.length;
        int target = 0;
        for(int i = 0; i < n; i ++)
            target += nums[i];
        if(target % 2 != 0) return false;
        target /= 2;
        boolean[] f = new boolean [target+1];
        f[0] = true;
        for(int i = 1; i <= n; i ++) {
            for(int j = target; j >= nums[i-1]; j --) {
                f[j] = f[j - nums[i-1]] || f[j];
            }
        }
        return f[target];
    }
}

完全背包

在这里插入图片描述

外层是物品(数组)遍历,内层是限制(容量价值)遍历

279. 完全平方数

在这里插入图片描述

完全背包问题

class Solution {
    public int numSquares(int n) {
        int[] f = new int[n+1];
        f[1] = 1;  
        for(int i = 1; i <= n; i ++) {
            f[i] = i; // 最坏的情况就是每次+1
            for(int j = 1 ;j <= n; j ++)
                if(i >= j*j)
                    f[i] = Math.min(f[i], f[i - j*j] + 1);
        }
        return f[n];
    }
}

322. 零钱兑换

在这里插入图片描述

也是完全背包问题

for(int i = 1; i <= n; i ++ ) {
    for(int j = c[i-1]; j <= amount; j ++) {
        dp[j] = Math.min(dp[j], dp[j-c[i-1]] + 1);
    }
}

Arrays.fill(dp, amount + 1); //因为是最小值,初始化0会一直是0

class Solution {
    public int coinChange(int[] c, int amount) {
        //dp[i]:表示最小个数
        //dp[j] = min(dp[j], dp[j-c[i]] + 1)
        int n = c.length;
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1); //因为是最小值,初始化0会一直是0
        dp[0] = 0;
        for(int i = 1; i <= n; i ++ ) {
            for(int j = c[i-1]; j <= amount; j ++) {
                dp[j] = Math.min(dp[j], dp[j-c[i-1]] + 1);
            }
            
        }
        int ans = dp[amount];
        return ans < amount + 1 ? ans : -1;
    }
}

两个字符串DP

LCR 095. 最长公共子序列

在这里插入图片描述

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
    //设 f(i,j)表示第一个字符串的前 i个字符和第二个字符串的前 j个字符的最长公共子序列
        //dp[i][j] = max(dp[i-1][j], dp[i][j-1],dp[i-1][j-1] + 1) 或者 max(dp[i-1][j], dp[i][j-1])
        //忽略text1[i]或者忽略text2[j]
        //这个是 求两个字符串的公共子序列,不需要len 不是遍历len对len分1 2 更长的情况的
        int n = text1.length(), m = text2.length();
        int[][] dp = new int [n+1][m+1];
        int ans = 0;
        for(int i = 1; i <= n; i ++) {
            for(int j = 1; j <= m; j ++) {
                if(Objects.equals(text1.charAt(i-1),text2.charAt(j-1))) 
                    dp[i][j] = Math.max(Math.max(dp[i-1][j], dp[i][j-1]),dp[i-1][j-1] + 1);
                else 
                    dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
            }
        }
        return dp[n][m];//两个字符串的最长公共子序列直接返回f[n][m].因为公共子序列后面的一定比前面的长。但是递增子序列就不一定了

    }
}

也不一定两个字符串就 设两变量f[i,j]这样

139. 单词拆分

在这里插入图片描述
f[i]:前i个能否被拆分【这个问题其实是单个字符串,从set里匹配单个字符串】
如果能找到j<i f[j]=true 并且 s[j~i]在words里面,那么f[i]也能被拆分

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> words = new HashSet<>(wordDict);
        //f[i]:前i个能否被拆分
        int n = s.length();
        boolean[] f = new boolean[n+1];
        f[0] = true;
        for(int i = 1; i <= n; i ++) {
            //如果能找到j f[j]=true 并且 s[j~i]在words里面,那么f[i]也能被拆分
            for(int j = i-1; j >= 0; j --) { //因为前面都已经匹配了,这样更快
                if(f[j] && words.contains(s.substring(j, i))) 
                    f[i] = true;
            }
        }
        return f[n];
    }
}

JAVA的substring是开始位置,终止位置。终止位置是开区间不包含他

单个数组字符串DP

5. 最长回文子串

单个数组,dp[i][j] = dp[i+1][j-1] && s[i]==s[j]

遍历len len=2需要特判

class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n + 1][n + 1];
        //dp[i][j]:字符串相关一般都是从i到j
        //dp[i][j] = dp[i+1][j-1] && s[i]==s[j]
        int res = 0;
        String resStr = "";
        for(int i = 0; i <= n; i ++){
            dp[i][i] = true;
        }
        
        for(int len = 1; len <= n; len ++) {
            for(int i = 0; i + len -1 < n; i ++) {
                int j = i + len -1;
                if(len == 1) {
                    dp[i][j] = true;
                }
                else if(len == 2) {
                    dp[i][j] = s.charAt(i)== s.charAt(j);
                }
                else {
                    dp[i][j] = dp[i+1][j-1] && (s.charAt(i)==s.charAt(j)); 
                }
                if(dp[i][j] && res < len) {
                    res = len; resStr = s.substring(i,j + 1);
                    //substring(int beginIndex, int endIndex) 
                }
            }
        }
        return resStr;
    }
}

300. 最长递增子序列

单个数组, dp[i] = Math.max(dp[i], dp[j] + 1)

public int lengthOfLIS(int[] nums) {
    //dp[i]:到i的最长递增子序列
    //dp[i] = dp[j]+1 
    int n = nums.length;
    int[] dp = new int [n+1];
    int ans = 0;
    for(int i = 1; i <= n ; i ++ ) {
        dp[i] = 1;
        for(int j =1; j <= n; j ++ ) {
            if(nums[i-1] > nums[j-1]) {
                dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }
        ans = Math.max(ans, dp[i]);//位置
    }
    return ans;
}

53.最大子数组和

请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组是数组中的一个连续部分。

dp[i] = Math.max(dp[i-1], 0) + nums[i]

class Solution {
    public int maxSubArray(int[] nums) {
        int dp[] = new int[nums.length+1];
        int res  = nums[0];
        dp[0] = Math.max(nums[0],0);
        for(int i = 1; i < nums.length; i ++) {
            dp[i] = Math.max(dp[i-1], 0) + nums[i];
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

152. 乘积最大子数组

要求连续:

由于存在负数,那么会导致最大的变最小的,最小的变最大的。因此还需要维护当前最小值imin
同时求解当前最大值和当前最小值

由于f[i]只和f[i-1] g[i-1]有关,可用两个变量存

class Solution {
    public int maxProduct(int[] nums) {
        int n = nums.length;
        int ans = Integer.MIN_VALUE;
        int imax = 1; 
        int imin = 1;
        for(int i = 1; i <= n; i ++ ) {
            if(nums[i-1] < 0){ 
              int tmp = imax;
              imax = imin;
              imin = tmp;
            }
            imax = Math.max(imax * nums[i-1], nums[i-1]);
            imin = Math.min(imin * nums[i-1], nums[i-1]);
            
            ans = Math.max(ans, imax);
        }
        return ans;
    }
}

198. 打家劫舍

在这里插入图片描述
要第i家的最大金额:s[i] = u[i-1]+nums[i]; 则一定不要第i-1家
不要第i家的最大金额:u[i]=max(u[i-1], s[i-1]) 也可能不要第i-1家,也可能要

class Solution {
    public int rob(int[] nums) {
        //要:s[i] = u[i-1]+nums[i]; 不要u[i]=max(u[i-1], s[i-1])
        int len = nums.length;
        int[] s = new int[len], u = new int[len];
        s[0] = nums[0];
        u[0] = 0;
        int ans = s[0];
        for(int i = 1; i < len; i ++) {
            s[i] = u[i-1] + nums[i];
            u[i] = Math.max(u[i-1], s[i-1]);
            ans = Math.max(ans, s[i]);
            ans = Math.max(ans, u[i]);
        }
        return ans;
    }
}

三角形

120. 三角形最小路径和

在这里插入图片描述

注意两种,只有一条路,j=0和对角线

dp[i][j] = min(dp[i-1][j-1],dp[i-1][j]) + triangle[i][j]

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        //dp[i][j]:到达(i,j)的最小路径和
        //dp[i][j] = min(dp[i-1][j-1],dp[i-1][j]) + triangle[i][j]
        int n = triangle.size();
        //边界没处理好
        
        int[][] dp = new int[n+1][n+1];
        for(int i = 1; i <= n; i ++ ) {
            dp[i][0] = triangle.get(i-1).get(0) + dp[i-1][0];//没有(i-1,j-1)那一条路了
            for(int j = 1; j <= i; j ++) {
                if(j == i) dp[i][j] = dp[i-1][j-1] + triangle.get(i-1).get(j-1);//灭有(i-1,j)那条路 对角线
                else dp[i][j] = Math.min(dp[i-1][j-1],dp[i-1][j]) + triangle.get(i-1).get(j-1);
            }
        }
        int ans = Integer.MAX_VALUE;
        for(int i = 1; i <= n; i++) {
            ans = Math.min(ans, dp[n][i]);
        }
        return ans;
    }
}

杨辉三角:
在这里插入图片描述

贪心

121. 买卖股票的最佳时机

遍历所有天i,对于第i天,记录前i天的最小值minn,结果是max(Prices[i]-minn)

class Solution {
    public int maxProfit(int[] prices) {
        int ans = 0;
        int minn = 100000;
        for(int i = 0; i < prices.length; i ++) {
            minn = Math.min(minn, prices[i]);//前i天的最小值也可以
            ans = Math.max(prices[i]-minn, ans);
        }
        return ans;
    }
}

55. 跳跃游戏

在这里插入图片描述

class Solution {
    public boolean canJump(int[] nums) {
       //dp[i]:nums[i]之前能跳到的最远距离,含i
       //跳到dp[i]: i + nums[i]
       //不跳到dp[i]: dp[i-1] i-1之前能跳的最远距离
       int[] dp = new int [nums.length + 1];
       for(int i = 1; i < nums.length; i ++) {//不用管最后一个
            dp[i] = Math.max(dp[i-1], i + nums[i-1]);
            if(dp[i] < i + 1) return false;//不用管最后一个,因为dp[i]肯定能跳到他后一个
       }
       return true;
    }
}

贪心做法:
while(i > last && i > last + nums[last]) last++;
if(last == i) return false; 说明i前面没有一个满足 i <= last + nums[last]的---->说明没有跳板能跳到i的

class Solution {
    public boolean canJump(int[] nums) {
       for(int i = 1, last = 0; i < nums.length; i ++) {
            while(i > last && i > last + nums[last])  last++;
            if(last == i) return false;//说明i前面没有一个满足 i <= last + nums[last]的---->说明没有跳板能跳到i的
       }
       return true;
    }
}

45. 跳跃游戏 II

在这里插入图片描述

dp[i]: 跳到nums[i]的最小次数
从i不能跳到last, last++;
从i能跳到last,那么,dp[i]就是dp[last]+1。无论last离i多远
在这里插入图片描述

class Solution {
    public int jump(int[] nums) {
        //dp[i]: 跳到nums[i]的最小次数
       int[] dp = new int [nums.length];
       for(int i = 1, last = 0; i < nums.length; i ++) {
       //从1开始,dp[0]=0啦,last从0计算dp[1]
            //从i不能跳到last, last++;
            //从i能跳到last,那么,dp[i]就是dp[last]+1。无论last离i多远
            while(i > last && i > last + nums[last])  last ++;
            dp[i] = dp[last] + 1;
       }
       return dp[nums.length-1];
    }
}

区间问题

763. 划分字母区间

在这里插入图片描述

在这里插入图片描述
end == i // 当前区间合并完毕
end, start分别存储当前区间的终止位置的最大值,当前区间的起始位置

class Solution {
    public List<Integer> partitionLabels(String S) {
        char[] s = S.toCharArray();
        int n = s.length;
        int[] last = new int[26];
        for (int i = 0; i < n; i++) {
            last[s[i] - 'a'] = i; // 每个字母最后出现的下标
        }

        List<Integer> ans = new ArrayList<>();
        int end = 0, start = 0;// 记录当前这一段的起始位置和终止位置
        for (int i = 0; i < n; i++) {
            end = Math.max(end, last[s[i] - 'a']); // 更新当前区间右端点的最大值
            if (end == i) { // 当前区间合并完毕
                ans.add(end - start + 1); // 区间长度加入答案
                start = i + 1; // 下一个区间的左端点. 因为i是一直变的
            }
        }
        return ans;
    }
}

56. 合并区间

class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals, (p, q) -> p[0] - q[0]); // 按照左端点从小到大排序
        List<int[]> res = new ArrayList<>();
        
        int l = intervals[0][0], r = intervals[0][1];
        for(int i = 1; i < intervals.length; i ++) {
            int newl = intervals[i][0], newr = intervals[i][1];
            if(newr < r) //包含 
                continue;
            else if(newl <= r && newr >= r) {
                r = newr;
            }
            else if(newl > r) {//两段不重叠
                res.add(new int[]{l,r});
                l = newl;
                r = newr;
            }
        }
        // 最后一个合并后的区间需要添加到结果中
        res.add(new int[]{l, r});
        
        // 将 List<int[]> 转换为 int[][] 并返回
        return res.toArray(new int[res.size()][]);
    }
}

http://www.kler.cn/a/453900.html

相关文章:

  • 金融租赁系统的发展与全球化战略实施探讨
  • 嵌入式科普(25)Home Assistant米家集成意味着IOT的核心是智能设备
  • Flink的Watermark水位线详解
  • Java线程池面试题
  • 【ANGULAR网站开发】初始环境搭建
  • Blender真实灰尘粒子动画资产预设 Dust Particles Pro V1.2
  • 24 go语言(golang) - gorm框架安装及使用案例详解
  • 什么是ondelete cascade以及使用sqlite演示ondelete cascade使用案例
  • apisix的hmac-auth认证
  • 【每日学点鸿蒙知识】图片控件对齐、上架的应用无法拉起应用详情页、RotateOptions配置、签名配置问题、弹框背景色
  • Leetcode 200 Number of Islands
  • c++最大公约数和最小公倍数的深入剖析
  • Oracle Database 23ai 中的DBMS_HCHECK
  • AWS Certified AI Practitioner 自学考试心得
  • 关于FPGA的IO三引脚形式
  • 【YOLO】(基础篇一)YOLO介绍
  • TiDB 的MPP架构概述
  • Python进阶之opencv图片和视频基本读取关闭
  • Java后端开发 ”Bug“ 分享——订单与优惠卷
  • 离心式压缩机设计的自动化方法
  • matlab中的cell
  • 【每日学点鸿蒙知识】类型判断、three.js支持情况、Grid拖动控制、子窗口路由跳转、真机无法断点
  • OpenHarmony 3.2 调用获取指定网络接口信息报错,DHCP报错:callback error 29189
  • 人工智能python快速入门
  • 初始化全部推断的寄存器、 SRL 和存储器
  • 两分钟掌握 TDengine 全部写入方式