【LeetCode 热题 100】动态规划 系列

news2025/7/19 16:12:37

📁 70. 爬楼梯

        状态标识:爬到第i层楼梯时,有多少种方法。

        状态转移方程:dp[i] = dp[i-1] + dp[i-2],表示从走一步和走两步的方式。

        初始化:dp[1] = 1 , dp[2] = 2。

        返回值:dp[n],即走到第n层可以有多少种爬法。

class Solution {
public:
    int climbStairs(int n) {
        int dp[50] = { 0  , 1 , 2};

        for(int i = 3 ; i <= n ; ++i)
        {
            dp[i] = dp[i-1] + dp[i-2];
        }

        return dp[n];
    }
};

📁 118. 杨辉三角

        就是一道找规律的题目,每一列第一个和每一行最后一个初始化为1,其余的为左上方和右上方数之和。

        状态转移方程:dp[i][j] = dp[i-1][j] + dp[i-1][j-1]。

vector<vector<int>> generate(int numRows) {
        vector<vector<int>> dp(numRows);

        for(int i = 0 ; i < numRows ; ++i)
            dp[i].resize(i + 1) , dp[i][i] = 1 , dp[i][0] = 1;

        for(int i = 2 ; i < numRows ; ++i)
            for(int j = 1 ; j < i ; ++j)
                dp[i][j] = dp[i-1][j] + dp[i-1][j-1];

        return dp;
    }

📁 198. 打家劫舍

        状态表示:dp[i]表示偷窃到第 i 加时,此时偷取到的最大金额。

        转移方程:dp[i] = max(dp[i-1] , dp[i-2] + num[i-1])

        初始化:dp[0] = nums[1],第一家是可以直接偷取到的。

        返回值:返回dp[n],即偷取到最后一家是此时的最大金额。

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n + 1 , 0);
        dp[1] = nums[0];

        for(int i = 2 ; i <= n ; ++i)
            dp[i] = max(dp[i-1] , dp[i-2] + nums[i-1]);
        return dp[n];
    }
};

📁 279. 完全平方数

        状态标识:dp[i]表示和为 i 的完全平方数的最小数量。 

        转移方程:dp[i] = min(dp[i] , dp[i - j * j] + 1)。j * j 表示小于i的完全平方数 

        初始化:dp[i] = i,将当前数字更新为最大结果,即最坏结果,表示 i 可以由 i 个 1 组成

        返回值:返回dp[n]。

        动态规划本身就是一个递推的过程,我们从前往后枚举,当枚举到第i个数的时候,意味着i-1都被枚举完了。因此,当一个数减去一个完全平方数时剩余和一定能在dp数组中找到,并且剩余和已经知道最小完全平方数的最小数量,因此,我们结果只需要在此基础上+1即可。

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1 , 0);

        for(int i = 1 ; i <= n ; ++i)
        {
            dp[i] = i;
            for(int j = 2 ; j * j <= i ; ++j)
            {
                dp[i] = min(dp[i] , dp[i-j*j] + 1);
            }
        }

        return dp[n];
    }
};

📁 322. 零钱兑换

        这道题可以看做是上一题的一种变形。

        状态标识:dp[i]表示金额为 i 的时,需要的最少硬币数量。 

        转移方程:dp[i] = min(dp[i] , dp[i - coin] + 1)。

        初始化:dp[0] = 0,其余将当前数字更新0x3f3f3f3f,表示目前不能通过硬币各个金额组成选择不能组成(方便返回值的处理)

        返回值:返回dp[amoun]。需要注意的是,如果dp[amoun] = 0x3f3f3f3f,需要返回-1。

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount + 1, 0x3f3f3f3f);
        dp[0] = 0;

        for(int i = 1 ; i <= amount ; ++i)
        {
            for(auto coin : coins)
                if(coin <= i)
                    dp[i] = min(dp[i] , dp[i-coin] + 1);
        }

        return dp[amount]==0x3f3f3f3f ? -1 : dp[amount];
    }
};

📁 139. 单词拆分

        状态表示:以某个字符为结尾的字符串,能否由wordDict中的word拼接形成。为了快速查找字典里面的单词,我们是用哈希系列容器存储单词。

        转移方程:dp[i] = str(j , i)是字典里面的单词 &&  dp[j - 1]。

        初始化:dp[0] = true,表示只有一个字符,并且成功在哈希表中找到。

        返回值:dp[n],以n为结尾的字符串,能否由wordDict中的word拼接形成。

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        int n = s.size();
        vector<int> dp(n + 1 , false);
        dp[0] = true;

        unordered_set<string> hash;
        for(auto word : wordDict)
            hash.insert(word);

        for(int i = 1 ; i <= n ; ++i)
        {
            for(int j = i; j > 0 ; --j)
            {
                string tmp = s.substr(j - 1, i - j + 1);
                if(hash.find(tmp) != hash.end())
                    dp[i] = dp[j - 1];
                if(dp[i] == true)
                    break;
            }
        }

        return dp[n];
    }
};

📁 300. 最长递增子序列

        如果想要有一个子序列尽可能的多,那么就要使得子序列中元素相较于前一个元素上升趋势尽可能小,与后一个元素上升趋势尽可能大。通过一个数组dp来维护递增子序列。

        1. 当新来一个元素时,如果大于尾部元素,直接插入;

        2. 如果小于尾部元素,那么之前就一定存在比他的的一个或者多个元素,因此我们将这个新的较小值更新到子序列中第一个比它的的元素中,使得子序列上升趋势变得更小。

        使用二分查找算法,查找子序列中第一个比新来较小值大的的元素,然后更新。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = nums.size();
        vector<int> ret;
        ret.push_back(nums[0]);

        for(int i = 1 ; i < n ; ++i)
        {
            if(nums[i] > ret.back())
                ret.push_back(nums[i]);
            else
            {
                int left = 0 , right = ret.size() - 1;

                while(left < right)
                {
                    int mid = (left + right) >> 1;
                    if(ret[mid] < nums[i])
                        left = mid + 1;    
                    else
                        right = mid;
                }
                ret[left] = nums[i];
            }
        }

        return ret.size();
    }
};

📁 152. 乘积最大子数组

        遍历数组,每次遍历时,记录乘积最大值,更新结果。问题是乘积最大值怎么更新?

        当一个新元素x到来时,与之前连续子数组乘积最大值maxS进行比较 max( x , maxS)。

        1. 如果 x 是正数,且maxS也是正数,那么会得到一个更大的正数。

        2. 如果 x 是正数,maxS是负数,那么会得到一个更小的数值。

        3. 如果 x 是负数,maxS是正数,那么会得到一个更小的数值。

         4. 如果 x 是负数,maxS是负数,那么会得到一个更大的数值。

        但如果我们记录一个较小值minS,那么 x 如果负数,minS也是负数,就会得到一个更大的值。

        即,如果x是正数,必须有一个最大值相乘才能得到更大值;如果x是负数,必须和一个最小值相乘才能得到一个更大值。

        核心:负数 x 大=小;负数 x 小=大;正数 x 大=大;正数 x 小=小

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int ans = nums[0] , maxI = nums[0] , minI = nums[0];
        int n = nums.size();

        for(int i = 1 ; i < n ; ++i)
        {
            int tmp = maxI;

            /*
                nums[i] * tmp: 如果nums[i]是正数, 得到更大值
                nums[i] * minI: 如果nums[i]是负数, 得到更大值
            */
            maxI = max(max(nums[i] , nums[i] * tmp) , nums[i] * minI);

            /*
                nums[i] * minI: 如果nums[i]是正数, 得到更小值
                nums[i] * tmp: 如果nums[i]是负数, 得到更小值
            */
            minI = min(min(nums[i] , nums[i] * minI) , nums[i] * tmp);

            ans = max(ans , maxI);
        }

        return ans;
    }
};

📁 416. 分割等和子集

        就是一道简单的01背包问题。首先判断个数组总和是否为偶数,如果是奇数直接返回false,因为不可能将数组分割成元素和相等的两个子集;如果sum是偶数,令target = sum / 2。需要判断从数组中能否选出一些数,使得这些数之和=target。

        状态标识:dp[i][j]: 前i个元素中能否恰好选出 总和为 j 的子集

        转移方程:dp[i][j] = (不选第i件,dp[i-1][j]) or (选第i件 dp[i-1][j-nums[i]])

        返回值:dp[n][target]。

        此外,采用滚动数组进行优化,使得只能一维数组就可以完成上述操作,只不是从前往后遍历改成往后往前遍历。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for(auto e : nums)
            sum += e;
        if(sum % 2 != 0)
            return false;

        int n = nums.size();
        int target = sum / 2;
        vector<bool> dp(target + 1 , false);
        dp[0] = true;
    
        for(int i = 1 ; i <= n ; ++i)
        {
            for(int j = target ; j >= nums[i-1] ; --j)
                dp[j] = dp[j] || dp[j- nums[i-1]];
        }

        return dp[target];
    }
};

📁 32. 最长有效括号

    状态表示: dp[i]表示 以s[i]为结尾并且包含s[i]的最长有效括号。

    状态转移方程:

        1. s[i] =='(' 无法组成有效括号 dp[i] = 0;

        2. s[i] ==')' 取决于之前的有效括号

         情况1: s[i-1] == '('

            dp[i] = dp[i-2] + 2;

         情况2:  s[i-1] == ')'

            这里如果想要包含s[i]组成有效括号的字符串

            必须满足 ((...)) 这种形式 , 其中...是未知个数的有效括号子串。

                    |

                    v

            即 s[i - dp[i-1] - 1] == '('

            所以, dp[i] = dp[i-1] + dp[i - dp[i-1] - 2 ] + 2;

    初始化:

        dp[0] = 0 一个字符不能构成有效括号

class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.size();
        int ans = 0;
        vector<int> dp(n + 1 , 0);
        
        for(int i = 1 ; i <= n ; ++i)
        {
            if(s[i] == ')')
            {
                if(s[i-1] == '(')
                    dp[i] = (i - 2 >= 0 ? dp[i-2] : 0) + 2;
                else if(i - dp[i-1] - 1 >= 0 && s[i - dp[i-1] - 1] == '(')
                    dp[i] = dp[i - 1] + 2 + (i - dp[i-1] - 2 >= 0 ? dp[i - dp[i-1] - 2] : 0);
            }
            ans = max(ans , dp[i]);
        }

        return ans;
    }
};

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

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

相关文章

计网实验笔记(一)CS144 Lab1

Lab0 ByteStream : 实现一个在内存中的 有序可靠字节流Lab1 StreamReassembler&#xff1a;实现一个流重组器&#xff0c;一个将字节流的字串或者小段按照正确顺序来拼接回连续字节流的模块Lab2 TCPReceiver&#xff1a;实现入站字节流的TCP部分。Lab3 TCPSender&#xff1a;实…

使用 OpenCV 将图像中标记特定颜色区域

在计算机视觉任务中&#xff0c;颜色替换是一种常见的图像处理操作&#xff0c;广泛用于视觉增强、目标高亮、伪彩色渲染等场景。本文介绍一种简单而高效的方式&#xff0c;基于 OpenCV 检测图像中接近某种颜色的区域&#xff0c;并将其替换为反色&#xff08;对比色&#xff0…

智源联合南开大学开源Chinese-LiPS中文多模态语音识别数据集

2025年5月6日&#xff0c;智源研究院在法国巴黎举行的GOSIM全球开源创新论坛上发布Chinese-LIPS中文多模态语音识别数据集&#xff0c;该数据为智源研究院联合南开大学共同构建。 在语音识别技术飞速发展的背景下&#xff0c;多模态语音识别正逐步成为学术界和工业界的研究热点…

RabbitMQ最新入门教程

文章目录 RabbitMQ最新入门教程1.什么是消息队列2.为什么使用消息队列3.消息队列协议4.安装Erlang5.安装RabbitMQ6.RabbitMQ核心模块7.RabbitMQ六大模式7.1 简单模式7.2 工作模式7.3 发布订阅模式7.4 路由模式7.5 主题模式7.6 RPC模式 8.RabbitMQ四种交换机8.1 直连交换机8.2 主…

python爬虫实战训练

前言&#xff1a;哇&#xff0c;今天终于能访问豆瓣了&#xff0c;前几天爬太多次了&#xff0c;网页都不让我访问了&#xff08;要登录&#xff09;。 先来个小练习试试手吧&#xff01; 爬取豆瓣第一页&#xff08;多页同上篇文章&#xff09;所有电影的排名、电影名称、星…

Redis(三) - 使用Java操作Redis详解

文章目录 前言一、创建项目二、导入依赖三、键操作四、字符串操作五、列表操作六、集合操作七、哈希表操作八、有序集合操作九、完整代码1. 完整代码2. 项目下载 前言 本文主要介绍如何使用 Java 操作 Redis 数据库&#xff0c;涵盖项目创建、依赖导入及 Redis 各数据类型&…

【全网首发】解决coze工作流批量上传excel数据文档数据重复的问题

注意&#xff1a;目前方法将基于前一章批量数据库导入的修改&#xff01;&#xff01;&#xff01;&#xff01;请先阅读上篇文章的操作。抄袭注明来源 背景 上一节说的方法可以批量导入文件到数据库&#xff0c;但是无法解决已经上传的条目更新问题。简单来说&#xff0c;不…

xss-labs靶场第11-14关基础详解

前言&#xff1a; 目录 第11关 第12关 第13关前期思路&#xff1a; 第十四关 内容&#xff1a; 第11关 也和上一关一样&#xff0c;什么输入框都没有&#xff0c;也就是 也是一样的操作&#xff0c;先将这里的hidden属性删掉一个&#xff0c;注意是删掉一个 输入1111&a…

ConcurrentSkipListMap的深入学习

目录 1、介绍 1.1、线程安全 1.2、有序性 1.3、跳表数据结构 1.4、API 提供的功能 1.5、高效性 1.6、应用场景 2、数据结构 2.1、跳表&#xff08;Skip List&#xff09; 2.2、节点类型&#xff1a; 1.Node 2.Index 3.HeadIndex 2.3、特点 3、选择层级 3.1、随…

XML简要介绍

实际上现在的Java Web项目中更多的是基于springboot开发的&#xff0c;所以很少再使用xml去配置项目。所以我们的目的就是尽可能快速的去了解如何读懂和使用xml文件&#xff0c;对于DTD&#xff0c;XMLSchema这类约束的学习可以放松&#xff0c;主要是确保自己知道这里面的大致…

什么是直播美颜SDK?美颜技术底层算法科普

当下&#xff0c;不论是社交直播、电商直播&#xff0c;还是线上教学、虚拟主播场景&#xff0c;都离不开美颜技术的加持。虽然大家在日常使用直播APP时经常体验到美颜效果&#xff0c;但背后的技术原理却相对复杂。本篇文章小编将为大家揭开直播美颜SDK的神秘面纱&#xff0c;…

【pbootcms】打开访问首页显示未检测到您服务器环境的sqlite3数据库拓展,请检查php.ini中是否已经开启该拓展

【pbootcms】新建网站&#xff0c;新放的程序&#xff0c;打开访问首页显示未检测到您服务器环境的sqlite3数据库拓展&#xff0c;请检查php.ini中是否已经开启该拓展。 检查目前网站用到哪个版本的php&#xff0c;然后打开相关文件。 修改一下内容&#xff1a; 查找sqlite3,…

MySQL——十、InnoDB引擎

MVCC 当前读&#xff1a; 读取的是记录的最新版本&#xff0c;读取时还要保证其他并发事务不能修改当前记录&#xff0c;会对读取的记录进行加锁。 -- 当前读 select ... lock in share mode(共享锁) select ... for update update insert delete (排他锁)快照读&#xff1a;…

visual studio生成动态库DLL

visual studio生成动态库DLL 创建动态库工程 注意 #include “pch.h” 要放在上面 完成后点击生成 创建一个控制台项目 设置项目附加目录为刚才创建的动态库工程Dll1&#xff1a; 配置附加库目录&#xff1a; 配置动态库的导入库&#xff08;.lib&#xff09;&#xff1a;链…

IDEA中git对于指定文件进行版本控制

最近在自己写代码的时候遇到了和公司里面不一样的&#xff0c;自己写的代码推到码云上是&#xff0c;会默认对于所有修改都进行提交&#xff0c;这样再提交的时候很不方便。 问了问ai&#xff0c;表示可以手动创建脚本实现&#xff0c;但是ai曲解了我的意思&#xff0c;它实现…

用Python绘制梦幻星空

用Python绘制梦幻星空 在这篇教程中&#xff0c;我们将学习如何使用Python创建一个美丽的星空场景。我们将使用Python的图形库Pygame和随机库来创建闪烁的星星、流星和月亮&#xff0c;打造一个动态的夜空效果。 项目概述 我们将实现以下功能&#xff1a; 创建深蓝色的夜…

GEE计算 RSEI(遥感生态指数)

&#x1f6f0;️ 什么是 RSEI&#xff1f;为什么要用它评估生态环境&#xff1f; RSEI&#xff08;遥感生态指数&#xff0c;Remote Sensing Ecological Index&#xff09; 是一种通过遥感数据计算得到的、综合反映区域生态环境质量的指标体系。 它的设计初衷是用最少的变量&…

python的家教课程管理系统

目录 技术栈介绍具体实现截图系统设计研究方法&#xff1a;设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理&#xff0c;难度适中&#xf…

实现书签-第一部分

实现书签-第一部分 本节我们将实现书签功能&#xff0c;为菜谱点击类似于收藏的功能&#xff0c;然后可以在上方的书签找到我们所有收藏的书签&#xff1b; 在此之前&#xff0c;让我们修复一下之前的功能BUG&#xff0c;当我们搜索的时候&#xff0c;下面分页始终保持在上一…

解决将其他盘可用空间,移植到C盘

第一步首先下载安装 用来扩内存盘的实用工具资源-CSDN文库 第二步打开diskgenius.exe 第三步选中想扩容的盘 右击-》选择扩容分区-》选择要缩小的分区-》然后确定 第四步拖拽对勾的地方 或者在箭头地方输入想阔的大小&#xff0c;然后开始&#xff0c;一直确定&#xff0c;就…