codetop标签动态规划大全C++讲解(四)!!动态规划刷穿地心!!学吐了家人们o(╥﹏╥)o

news2024/11/29 10:45:40

一天复习一篇,个人学习记录

  • 1.最大子数组和
  • 2.最长的斐波那契子序列的长度
  • 3.最大正方形
  • 4.最长有效括号
  • 5.乘积最大子数组
  • 6.可被三整除的最大和
  • 7.回文子串数目
  • 8.最长回文子序列
  • 9.最长回文子串

1.最大子数组和

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

注意初始化,res需要初始为dp[0]

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        vector<int> dp(nums.size(), 0);
        dp[0] = nums[0];
        int res = dp[0];
        if(nums.size() <= 1) return dp[0];
        for(int i = 1; i < nums.size(); i ++){
            dp[i] = max(nums[i], dp[i - 1] + nums[i]);
            if(dp[i] > res) res = dp[i];
        }
        return res;
    }
};

2.最长的斐波那契子序列的长度

如果序列 X_1, X_2, …, X_n 满足下列条件,就说它是 斐波那契式 的:

  • n >= 3
  • 对于所有 i + 2 <= n,都有 X_i + X_{i+1} = X_{i+2}

给定一个严格递增的正整数数组形成序列 arr ,找到 arr 中最长的斐波那契式的子序列的长度。如果一个不存在,返回 0 。
eg:
输入: arr = [1,2,3,4,5,6,7,8]
输出: 5
解释: 最长的斐波那契式子序列为 [1,2,3,5,8] 。
dp[i][j] 表示以 arr[i] 和 arr[j] 作为最后两个数字的最长斐波那契式子序列的长度。

class Solution {
public:
    int lenLongestFibSubseq(vector<int>& arr) {
        int n = arr.size();
        unordered_map<int, int> indexMap;
        for (int i = 0; i < n; i++) {
            indexMap[arr[i]] = i;
        }

        vector<vector<int>> dp(n, vector<int>(n, 2));
        int maxLen = 0;

        for (int j = 1; j < n; j++) {
            for (int i = 0; i < j; i++) {
                int k = indexMap.find(arr[j] - arr[i]) != indexMap.end() ? indexMap[arr[j] - arr[i]] : -1;
                if (k >= 0 && k < i) {
                    dp[i][j] = dp[k][i] + 1;
                    maxLen = max(maxLen, dp[i][j]);
                }
            }
        }

        return maxLen > 2 ? maxLen : 0;
    }
};

3.最大正方形

在一个由0和1组成的二维矩阵内,找到只包含1的最大正方形,并返回其面积

前缀和做法:
和美团2024春招第一题平衡矩阵很像,给一个和平衡矩阵一样的做法
需要注意的是,在小美的平衡矩阵中,输入数组都是下标1开始,这里的matrix是从0开始的,而前缀和又必须从1开始,所以前缀和s要加的matrix[i - 1][j - 1] - ‘0’
左上角i,j的起始,已经是在前缀和s里面看了,所以从1开始而不是0

class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        if (matrix.empty()) return 0;
        int m = matrix.size(), n = matrix[0].size();
        vector<vector<int>> s(m+1, vector<int>(n+1, 0));
        int res = 0;
        
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                s[i][j] = (matrix[i-1][j-1] - '0') + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
            }
        }
        
        // i 和 j 是左上角, l 和 r 是右下角
        // len 是正方形边长
        for (int len = 1; len <= min(m, n); len++) {
            for (int i = 1; i <= m - len + 1; i++) {
                for (int j = 1; j <= n - len + 1; j++) {
                    int l = i + len - 1;
                    int r = j + len - 1;
                    int total = s[l][r] - s[l][j - 1] - s[i - 1][r] + s[i - 1][j - 1];
                    if (total == len * len) res = max(res, len * len);
                }
            }
        }
        return res;
    }
};

动态规划做法:

  1. dp[i][j] 代表(i,j)为右下角,且只包含1的正方形的边长最大值。
  2. 递推公式:
  • 如果该位置是0,则dp[i][j] = 0,因为当前位置不可能由1组成的正方形中
  • 如果该位置是1,则dp[i][j]的值由其上方、左方和左上方的三个相邻位置的dp值决定。具体而言,当前位置的元素值等于三个相邻位置的元素中的最小值加 1,状态转移方程如下:dp(i, j)=min(dp(i−1, j), dp(i−1, j−1), dp(i, j−1))+1
  1. 如果 i 和 j 中至少有一个为0,则位置(i,j)为右下角的最大正方形的边长只能是1,dp[i][j] = 1
class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        if(matrix.empty()) return 0;
        int m = matrix.size();
        int n = matrix[0].size();
        vector<vector<int>> dp(m, vector<int>(n, 0));
        int len = 0;
        for(int i = 0; i < m; i ++){
            for(int j = 0; j < n; j ++){
                if(matrix[i][j] == '1'){
                    if(i == 0 || j == 0) dp[i][j] = 1;
                    else dp[i][j] = min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1])) + 1;
                    if(dp[i][j] > len) len = dp[i][j];
                }
            }
        }
        return len * len;
    }
};

4.最长有效括号

给一个字符串只包含 ‘(’ 和 ‘)’ ,找出最长有效括号子串。
在这里插入图片描述
dp[i]的含义是以下标1结尾的字符串最长有效括号子串的长度
所以上图dp[1] = 2, dp[3] = 4

  1. 如果当前遍历到了‘(’,那么一定是非法序列,dp[i] = 0
  2. 如果当前遍历到了‘)’,那么分2种情况
  • )的前面是(,那么dp[i] = dp[i - 2] + 2
  • )的前面是),那么需要检查i - dp[i - 1] - 1,即前一个合法序列的前一个位置是不是左括号,类似于图中的dp[7],index = 7 的时候,此时 index - 1 也是右括号,我们需要知道 i - dp[i - 1] - 1 = 7 - dp [ 6 ] - 1 = 4 位置的括号的情况。而刚好 index = 4 的位置是左括号,此时 dp [ i ] = dp [ i - 1 ] + dp [ i - dp [ i - 1] - 2 ] + 2 (当前位置的前一个合法序列的长度,加上匹配的左括号前边的合法序列的长度,加上新增的长度 2)
class Solution {
public:
    int longestValidParentheses(string s) {
        vector<int> dp(s.size(), 0);
        int res = 0;
        for(int i = 1; i < s.size(); i ++){
            if(s[i] == ')'){
                if(s[i - 1] == '('){
                    dp[i] = (i - 2 >= 0 ? dp[i - 2] : 0) + 2;
                }else if(i > 0 && i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '('){
                    dp[i] = dp[i - 1] + (i - dp[i - 1] >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                }
            }
            res = max(res, dp[i]);
        }
        return res;
    }
};

5.乘积最大子数组

给你一个整数数组nums,请你找出数组中乘积最大的非空连续子数组,并将乘积返回
dpMax[i] 表示以第 i 个元素的结尾的子数组,乘积最大的值

  1. 当nums[i] >= 0 并且dpMax[i - 1] > 0,dpMax[i] = dpMax[i - 1] * nums[i]
  2. 当nums[i] >= 0 并且dpMax[i - 1] < 0,dpMax[i] = nums[i]
  3. 当nums[i] < 0时,dpMax需要分情况讨论
  • 当dpMin[i - 1] < 0,dpMax[i] = dpMin[i - 1] * nums[i]
  • 当dpMin[i - 1] >= 0,dpMax[i] = nums[i]

上面引入了dpMin数组,怎么求dpMin和dpMax其实是一样的。
首先
dpMax[i] = max(dpMax[i-1] * nums[i], dpMin[i-1] * nums[i], nums[i]);
求 dpMin[i] 同理
dpMin[i] = min(dpMax[i-1] * nums[i], dpMin[i-1] * nums[i], nums[i]);
由于都是上一个dpMax或者上一个dpMin推导的,所以可以不设数组
dpMax = max(dpMin * nums[i], max(dpMax * nums[i], (double)nums[i]));
dpMin = min(dpMin * nums[i], min(preMax * nums[i], (double)nums[i]));
如果是int的话会有一个测试用例过不了

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        if (n == 0) {
            return 0;
        }
        double dpMax = nums[0];
        double dpMin = nums[0];
        double maxProd = nums[0];
        for (int i = 1; i < n; i++) {
        	// 更新dpMin的时候需要dpMax之前的信息,所以先保存起来
            double preMax = dpMax;
            dpMax = max(dpMin * nums[i], max(dpMax * nums[i], (double)nums[i]));
            dpMin = min(dpMin * nums[i], min(preMax * nums[i], (double)nums[i]));
            maxProd = max(maxProd, dpMax);
        }
        return (int)maxProd; // 将结果转换回int,假设最终结果适合int类型。
    }
};

6.可被三整除的最大和

给你一个整数数组 nums,请你找出并返回能被三整除的元素 最大和。

dp[i][j]:处理前i个元素,余数为j的最大和。j有三种可能0,1,2按照定义,最终答案就是dp[n][0]
对于nums[i - 1],可选可不选

  1. 如果nums[i - 1]%3 = 0,那么说明加入当前nums[i - 1]不会改变其余数,所以:
  • dp[i][0] = max{dp[i - 1][0],dp[i - 1][0] + nums[i - 1]}
  • dp[i][1] = max{dp[i - 1][1],dp[i - 1][1] + nums[i - 1]}
  • dp[i][2] = max{dp[i - 1][2],dp[i - 1][2] + nums[i - 1]}
  1. 如果nums[i - 1]%3 = 1,那么说明加入当前nums[i - 1]之后,原来余数0会变成1,1变成2,2变成0
  • dp[i][0] = max{dp[i - 1][0],dp[i - 1][2] + nums[i - 1]}
  • dp[i][1] = max{dp[i - 1][1],dp[i - 1][0] + nums[i - 1]}
  • dp[i][2] = max{dp[i - 1][2],dp[i - 1][1] + nums[i - 1]}
  1. 如果nums[i − 1]%3 = 2,那么说明加入当前nums[ i − 1]之后,原来的余数0会变成2,1变成 0,2变成1:
  • dp[i][0] = max{dp[i - 1][0],dp[i - 1][1] + nums[i - 1]}
  • dp[i][1] = max{dp[i - 1][1],dp[i - 1][2] + nums[i - 1]}
  • dp[i][2] = max{dp[i - 1][2],dp[i - 1][0] + nums[i - 1]}
class Solution {
public:
    int maxSumDivThree(vector<int>& nums) {
        int n = nums.size();
        int dp[n + 1][3];// dp[i][j]代表下标0~i-1的数中,÷3余数为j的最大和

        memset(dp,0,sizeof dp);
        dp[0][1] = -1e9 , dp[0][2] = -1e9;// dp[0][0]是前0个数。÷3余数为0的最大和,是0

        for(int i = 1;i <= n;i++){
            int r = nums[i - 1] % 3;
            if(r == 0){
                dp[i][0] = max(dp[i - 1][0] , dp[i - 1][0] + nums[i - 1]);
                dp[i][1] = max(dp[i - 1][1] , dp[i - 1][1] + nums[i - 1]);
                dp[i][2] = max(dp[i - 1][2] , dp[i - 1][2] + nums[i - 1]);
            }
            else if(r == 1){
                dp[i][0] = max(dp[i - 1][0] , dp[i - 1][2] + nums[i - 1]);
                dp[i][1] = max(dp[i - 1][1] , dp[i - 1][0] + nums[i - 1]);
                dp[i][2] = max(dp[i - 1][2] , dp[i - 1][1] + nums[i - 1]);
            }
            else{
                dp[i][0] = max(dp[i - 1][0] , dp[i - 1][1] + nums[i - 1]);
                dp[i][1] = max(dp[i - 1][1] , dp[i - 1][2] + nums[i - 1]);
                dp[i][2] = max(dp[i - 1][2] , dp[i - 1][0] + nums[i - 1]);
            }
        }
        return dp[n][0];
    }
};

7.回文子串数目

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

如果设置 dp[i] 的意义是指以下标 i 结尾的字符串有 dp[i] 个回文串,那就会很难想,因为dp[i] 和 dp[i-1] ,dp[i + 1] 看上去都没啥关系
所以应该这样设置:bool类型dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。
假设s[i] != s[j],不是回文子串
s[i] == s[j],j - i <= 1,是回文串(a或aa的情况),如果 i 和 j 差距大于1,就撤回一步看dp[i + 1][j - 1]是不是回文子串

注意初始化,第二个for的j要从i开始不是i+1,差一步都不行

class Solution {
public:
    int countSubstrings(string s) {
        vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
        int res = 0;
        for(int i = s.size() - 1; i >= 0; i --){
            for(int j = i; j < s.size(); j ++){
                if(s[i] == s[j]){
                    if(j - i <= 1){
                        res++;
                        dp[i][j] = true;
                    }else{
                        if(dp[i + 1][j - 1]){
                            res++;
                            dp[i][j] = true;
                        }
                    }
                }
            }
        }
        return res;
    }
};

8.最长回文子序列

给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。
子序列,所以可以不连续

大致逻辑和3一样
在s[i] == s[j]时,dp[i][j] = dp[i + 1][j - 1] + 2
在s[i] != s[j]时,加入s[j]的回文子序列长度为dp[i + 1][j]。加入s[i]的回文子序列长度为dp[i][j - 1]。

从递推式可以看出来,i == j的情况没有覆盖,所以需要初始化为1

另外加入s长度只有一个的话,无法进入for循环,这个时候应该输出1,所以res也需要初始化为1

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
        int res = 1;
        for(int i = 0; i < s.size(); i ++) dp[i][i] = 1;
        for(int i = s.size() - 1; i >= 0; i --){
            for(int j = i + 1; j < s.size(); j ++){
                if(s[i] == s[j]) dp[i][j] = dp[i + 1][j - 1] + 2;
                else dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);

                if(dp[i][j] > res) res = dp[i][j];
            }
        }
        return res;
    }
};

9.最长回文子串

给你一个字符串 s,找到 s 中最长的 回文子串。
和3差不多

class Solution {
public:
    string longestPalindrome(string s) {
        vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
        int l = 0, maxLen = -1;
        for(int i = s.size() - 1; i >= 0; i --){
            for(int j = i; j < s.size(); j ++){
                if(s[i] == s[j]){
                    if(j - i <= 1) dp[i][j] = true;
                    else if(dp[i + 1][j - 1]) dp[i][j] = true;
                }
                if(dp[i][j] && j - i + 1 > maxLen){
                    maxLen = j - i + 1;
                    l = i;
                }
            }
        }        
        return s.substr(l, maxLen);
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2192000.html

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

相关文章

SpringBoot在线教育平台:设计与实现的深度解析

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

【JAVA开源】基于Vue和SpringBoot的教学资源库系统

本文项目编号 T 067 &#xff0c;文末自助获取源码 \color{red}{T067&#xff0c;文末自助获取源码} T067&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计5.4.1 管…

654、最大二叉树

1、题目描述 . - 力扣&#xff08;LeetCode&#xff09; 其实就是给定了一个所谓"最大二叉树"的规则&#xff0c;让我们去构建二叉树。 以 nums [3,2,1,6,0,5] 为例&#xff0c;规则如下&#xff1a; (1)找出其中的最大值6将其作为根节点&#xff0c;6前面的是左子…

自动驾驶系列—线控系统:驱动自动驾驶的核心技术解读与应用指南

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

工具 | 红队大佬亲测5款推荐的Burpsuite插件

*免责声明&#xff1a;* *本文章仅用于信息安全技术分享&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作…

基于SpringBoot+Vue+MySQL的校园二手物品交易系统

系统展示 用户前台界面 管理员后台界面 系统背景 校园二手物品交易系统开发的背景与重要性随着高等教育的蓬勃发展&#xff0c;大学生群体的规模持续扩大&#xff0c;随之而来的是物品更新换代速度的显著加快。学生们在追求新潮、高品质生活的同时&#xff0c;往往会产生大量闲…

微信步数C++

题目&#xff1a; 样例解释&#xff1a; 【样例 #1 解释】 从 (1,1) 出发将走 2 步&#xff0c;从 (1,2) 出发将走 4 步&#xff0c;从 (1,3) 出发将走 4 步。 从 (2,1) 出发将走 2 步&#xff0c;从 (2,2) 出发将走 3 步&#xff0c;从 (2,3) 出发将走 3 步。 从 (3,1) 出发将…

llm接口高可用工程实践(尽快关注我,以后这些文章将只对粉丝开放)

上一节课程链接&#xff1a;中文llama3仿openai api实战-CSDN博客 &#xff0c;本文是在上一节基础上继续操作 课程介绍 本文基于Chinese-LLaMA-Alpaca-3&#xff08;https://github.com/ymcui/Chinese-LLaMA-Alpaca-3&#xff09;项目&#xff0c;介绍如何通过搭建2个llama3…

生信初学者教程(二十五):验证候选特征

文章目录 介绍加载R包导入数据函数重要特征的表达ROC分析汇总筛选结果输出结果总结介绍 在成功识别出核心特征之后,为了验证这些特征的有效性和可靠性,我们在发现数据集和验证数据集上进行了进一步的评估。这一步骤旨在确保这些特征在不同数据集上的表达值具有一致性,并验证…

大模型时代下小模型知多少?从模型结构、预训练数据到运行时成本分析总结

今天&#xff0c;我们来谈谈小模型。《Small Language Models综述&#xff0c;Small Language Models: Survey, Measurements, and Insights》&#xff1a;https://arxiv.org/pdf/2409.15790这个工作&#xff0c;会有一些启发。 本文主要介绍三个话题&#xff0c;一个是小模型…

【笔记】信度检验

一、信度 信度是指测量结果的一致性和稳定性。 1.一致性&#xff08;Consistency&#xff09; 一致性指的是测量工具内部各个部分或项目之间的协调一致程度。高一致性意味着测量工具的不同部分都在测量同一个概念或特质。 例子&#xff1a;智力测试 假设我们有一个包含100…

成为AI产品经理,应该具备哪些条件?

开篇勘误标题&#xff1a;未来不会有AI产品经理这个岗位&#xff0c;就像没有移动产品经理一样。 如果你是个产品经理&#xff0c;但是不懂移动端的产品交互和设计&#xff0c;那你就只能在自己的头衔前面加上一个“PC”&#xff1a;PC产品经理&#xff0c;代表你的细分或者不…

在线Html到Markdown转换器

具体请前往&#xff1a;在线Html转Markdown

6个最受欢迎的大模型本地运行工具

运行大型语言模型 (LLM)&#xff08;如 ChatGPT 和 Claude&#xff09;通常涉及将数据发送到 OpenAI 和其他 AI 模型提供商管理的服务器。虽然这些服务是安全的&#xff0c;但一些企业更愿意将数据完全离线&#xff0c;以保护更大的隐私。 本文介绍了开发人员可以用来在本地运…

Java 枚举一口气讲完!(´▽`ʃ♡ƪ)

Java 枚举类型 Java面向对象设计 - Java枚举类型 什么是枚举类型&#xff1f; 枚举类型创建常量的有序列表作为类型。它以特定顺序指定常量。 在枚举类型中定义的常量是该枚举类型的实例。 语法 使用关键字enum使用以下语法定义枚举类型&#xff1a; <access-modifie…

Vue基础指令用法

vue2&#xff0c;官网&#xff1a;介绍 — Vue.js (vuejs.org) 例子&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-s…

【SpringBoot】基础+JSR303数据校验

目录 一、Spring Boot概要 1. SpringBoot介绍 2. SpringBoot优点 3. SpringBoot缺点 4. 时代背景-微服务 二、Spring Boot 核心配置 1. Spring Boot配置文件分类 1.1 application.properties 1.2 application.yml 1.3 小结 2. YAML概述 3. YAML基础语法 3.1 注意事…

生信初学者教程(二十六):特征和免疫浸润的关联分析

文章目录 介绍加载R包导入数据函数重要特征与免疫细胞的相关热图SLC6A8关联图SLC6A8与特定免疫细胞SLC6A8与其他免疫细胞输出结果总结介绍 在成功获取核心特征集之后,我们计划深入地探究这些特征与免疫浸润细胞之间的关联性,这是因为免疫浸润细胞在癌症的进程中扮演着至关重要…

成都睿明智科技有限公司抖音电商新蓝海的领航者

在当今这个数字化浪潮汹涌的时代&#xff0c;电商行业正以惊人的速度迭代升级&#xff0c;而抖音电商作为新兴势力&#xff0c;更是凭借其庞大的用户基数、精准的算法推荐和高度互动的社区氛围&#xff0c;成为了众多商家竞相追逐的蓝海市场。在这片充满机遇与挑战的海洋中&…

关于Excel将列号由字母改为数字

将Excel的列表由字母改为数字 步骤&#xff1a; 文件-选项-公式-勾选“使用公式”中的“R1C1引用样式(R)”-确定即可 部分步骤图示 设置前的样子 设置后的样子 虽然现在还不清楚在xlwings操作Excel时有什么作用&#xff0c;先留着吧。