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

news2024/12/27 12:30:48

文章目录

  • 背包
    • 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.coloradmin.cn/o/2266387.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

传统网络架构与SDN架构对比

传统网络采用分布式控制&#xff0c;每台设备独立控制且管理耗时耗力&#xff0c;扩展困难&#xff0c;按 OSI 模型分层&#xff0c;成本高、业务部署慢、安全性欠佳且开放性不足。而 SDN 架构将控制平面集中到控制器&#xff0c;数据转发由交换机负责&#xff0c;可统一管理提…

CI/CD是什么?

CI/CD 定义 CI/CD 代表持续集成和持续部署&#xff08;或持续交付&#xff09;。它是一套实践和工具&#xff0c;旨在通过自动化构建、测试和部署来改进软件开发流程&#xff0c;使您能够更快、更可靠地交付代码更改。 持续集成 (CI)&#xff1a;在共享存储库中自动构建、测试…

Vue中动态样式绑定+CSS变量实现切换明暗主题功能——从入门到进阶

1.直接借助Vue的动态绑定样式绑定 Vue动态样式绑定 在Vue中&#xff0c;动态样式绑定是一种强大的功能&#xff0c;它允许开发者根据数据的变化动态地更新元素的样式。以下是对Vue动态样式绑定的详细知识梳理与详解&#xff1a; 一、基础知识 Vue的动态样式绑定主要通过v-b…

科汛网校KesionEDU CheckOrder SQL注入漏洞复现

0x01 产品简介 科汛网校KesionEDU是KESION科汛开发的在线教育建站系统,支持在线直播教学、课程点播、录播授课等多种教学方式,满足不同场景下的教学需求。提供问答互动、学习点评、在线笔记等功能,增强学员与教师之间的互动交流。拥有在线考试系统,支持单选、多选、问答等…

Windows Subsystem for Linux——设置默认登录用户名

大纲 问题解法 问题 在《Windows Subsystem for Linux——安装多个相同的操作系统》一文中&#xff0c;我们实现了子系统的导出和导入&#xff0c;但是也带来了一个问题&#xff1a;登录到系统时&#xff0c;会使用root用户。在一些场景下&#xff0c;这并不符合我们的使用场景…

【编译原理】往年题汇总(山东大学软件学院用)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;编译原理_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. …

智慧农业物联网传感器:开启农业新时代

在当今科技飞速发展的时代&#xff0c;农业领域正经历着一场前所未有的变革&#xff0c;而智慧农业物联网传感器无疑是这场变革中的关键利器。它宛如农业的 “智慧大脑”&#xff0c;悄然渗透到农业生产的各个环节&#xff0c;为传统农业注入了全新的活力&#xff0c;让农业生产…

观察者模式和发布-订阅模式有什么异同?它们在哪些情况下会被使用?

大家好&#xff0c;我是锋哥。今天分享关于【观察者模式和发布-订阅模式有什么异同&#xff1f;它们在哪些情况下会被使用&#xff1f;】面试题。希望对大家有帮助&#xff1b; 观察者模式和发布-订阅模式有什么异同&#xff1f;它们在哪些情况下会被使用&#xff1f; 1000道 …

C# OpenCvSharp DNN 卡证检测矫正

目录 说明 效果 模型 项目 代码 下载 参考 说明 源码地址&#xff1a;https://modelscope.cn/models/iic/cv_resnet_carddetection_scrfd34gkps 在实人认证、文档电子化等场景中需要自动化提取卡证的信息&#xff0c;以便进一步做录入处理。这类场景通常存在两类问题&…

前端入门之VUE--ajax、vuex、router,最后的前端总结

前言 VUE是前端用的最多的框架&#xff1b;这篇文章是本人大一上学习前端的笔记&#xff1b;欢迎点赞 收藏 关注&#xff0c;本人将会持续更新。本人不是学前端的&#xff0c;这个是大一的时候上学的和做的笔记&#xff0c;那个时候学的也蒙&#xff0c;故这里对前端做一个总…

要查询 `user` 表中 `we_chat_subscribe` 和 `we_chat_union_id` 列不为空的用户数量

文章目录 1、we_chat_subscribe2、we_chat_union_id 1、we_chat_subscribe 要查询 user 表中 we_chat_subscribe 列不为空的用户数量&#xff0c;你可以使用以下 SQL 查询语句&#xff1a; SELECT COUNT(*) FROM user WHERE we_chat_subscribe IS NOT NULL;解释&#xff1a; …

RocketMQ的集群架构是怎样的?

大家好&#xff0c;我是锋哥。今天分享关于【RocketMQ的集群架构是怎样的?】面试题。希望对大家有帮助&#xff1b; RocketMQ的集群架构是怎样的? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 RocketMQ 是阿里巴巴开源的分布式消息中间件&#xff0c;广泛用于处…

使用DynadotAPI查看域名清仓中的过期域名列表

前言 Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮箱&…

uni-app 中使用微信小程序第三方 SDK 及资源汇总

&#x1f380;&#x1f380;&#x1f380;uni-app 跨端开发系列 &#x1f380;&#x1f380;&#x1f380; 一、uni-app 组成和跨端原理 二、uni-app 各端差异注意事项 三、uni-app 离线本地存储方案 四、uni-app UI库、框架、组件选型指南 五、uni-app 蓝牙开发 六、uni-app …

探索 Pencils Swap 的叙事:为 DeFi 的再次爆发蓄力

Pencils Protocol 最初是 Scroll 生态上一个综合性的 DeFi 平台&#xff0c;以 Farming、Vaults 以及 Auction 等系列产品板块为基础&#xff0c;其不仅成为了 Scroll 上重要的流动性、收益枢纽&#xff0c;同时也是重要的 LaunchPad 市场以及流量池&#xff0c;为 Scroll 生态…

基于STM32单片机矿井矿工作业安全监测设计

基于STM32单片机矿井矿工作业安全监测设计 目录 项目开发背景设计实现的功能项目硬件模块组成设计思路系统功能总结使用的模块技术详情介绍总结 1. 项目开发背景 随着矿井矿工作业环境的复杂性和危险性逐渐增加&#xff0c;矿井作业安全问题引起了社会各界的广泛关注。传统的…

数学建模与数学建模竞赛

什么是数学建模&#xff1f; 数学建模是通过数学的方法和工具&#xff0c;对现实世界的一个特定对象&#xff0c;依据其内在规律&#xff0c;做出一些必要的简化假设&#xff0c;从而建立一个数学结构的过程。数学建模的历史和数学的起源几乎同步开始&#xff0c;2000多年前&a…

stm32四联七段数码管,LED8*8点阵

一、七段数码管的整体代码和仿真 1&#xff09;代码 seg74.c #include "stm32f10x.h" // Device headervoid seg74_init(void) {GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(…

SpringCloudAlibaba技术栈-Dubbo

1、什么是Dubbo? 简单来说&#xff0c;dubbo就像是个看不见的手&#xff0c;负责专门从注册中心nacos调用注册到nacos上面的服务的&#xff0c;因为在微服务环境下不同的功能模块可能在不同的服务器上。dubbo调用服务就像是在调用本地的服务一样。 分布式调用与高并发处理 Du…

“AI智能安全管理系统:让安全无处不在

嘿&#xff0c;大家好&#xff01;今天咱们来聊聊一个超级酷炫又至关重要的东西——AI智能安全管理系统。想象一下&#xff0c;如果有一个系统可以像私人保镖一样24小时不间断地保护你和你的财产&#xff0c;是不是感觉特别安心&#xff1f;这就是AI智能安全管理系统带给我们的…