线性DP(动态规划)

线性DP的概念(视频)

学习线性DP之前,请确保已经对递推有所了解。

一、概念

1、动态规划

不要去看网上的各种概念,什么无后效性,什么空间换时间,会越看越晕。从做题的角度去理解就好了,动态规划就可以理解成一个 有限状态自动机,从一个初始状态,通过状态转移,跑到终止状态的过程。

2、线性动态规划

线性动态规划,又叫线性DP,就是在一个线性表上进行动态规划,更加确切的说,应该是状态转移的过程是在线性表上进行的。我们考虑有 0 到 n 这 n+1 个点,对于第 i 个点,它的值取决于 0 到 i-1 中的某些点的值,可以是求 最大值、最小值、方案数 等等。

很明显,如果一个点 i 可以从 i-1 或者 i-2 过来,求到达第 i 号点的方案数,就是我们之前学过的斐波那契数列了,具体可以参考这篇文章:递推。

二、例题解析

1、题目描述

给定一个 n,再给定一个 n(n ≤ 1000) 个整数的数组  cost, 其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦支付此费用,即可选择向上爬 1个 或者 2个 台阶。可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯,请计算并返回达到楼梯顶部的最低花费。

2、算法分析

我们发现这题和之前的爬楼梯很像,只不过从原来的计算 方案数 变成了计算 最小花费。尝试用一个数组来表示状态:f[i] 表示爬到第 i 层的最小花费。

由于每次只能爬 1个或者 2个台阶,所以 f[i] 这个状态只能从 f[i-1] 或者 f[i-2] 转移过来:

1)如果从 i-1 层爬上来,需要的花费就是 f[i-1] + cost[i-1];

2)如果从 i-2 层爬上来,需要的花费就是 f[i-2] + cost[i-2];

没有其他情况了,而我们要 求的是最小花费,所以 f[i] 就应该是这两者的小者,得出状态转移方程:

f[i] = min(f[i-1] + cost[i-1], f[i-2] + cost[i-2])

然后考虑一下初始情况 f[0] 和 f[1],根据题目要求它们都应该是 0。

3、源码详解

int min(int a, int b) {return a < b ? a : b;                   // (1)
}int minCostClimbingStairs(int* cost, int n){int i;                                  // (2)int f[1001] = {0, 0};                   // (3)for(i = 2; i <= n; ++i) {               // (4)f[i] = min(f[i-1] + cost[i-1], f[i-2] + cost[i-2]);}return f[n];                            // (5)
}
  1. (1) 为了方便求最小值,我们实现一个最小值函数 min,直接利用 C语言 的 条件运算符 就可以了;
  2. (2) 然后开始动态规划的求解,首先定义一个循环变量;
  3. (3) 再定义一个数组 f[i] 代表从第 0 阶爬到第 i 阶的最小花费,并且初始化第 0 项 和 第 1 项;
  4. (4) 然后一个 for 循环,从第 2 项开始,直接套上状态转移方程就能计算每一项的值了;
  5. (5) 最后返回第 n 项即可;

三、再谈动态规划

经典的线性DP有很多,比如:最长递增子序列、背包问题 是非常经典的线性DP了。建议先把线性DP搞清楚以后再去考虑其它的动态规划问题。

而作为动态规划的通解,主要分为以下几步:

    1、设计状态

    2、写出状态转移方程

    3、设定初始状态

    4、执行状态转移

    5、返回最终的解

一、基本概念

学习动态规划,如果一上来告诉你:最优子结构、重叠子问题、无后效性 这些抽象的概念,那么你可能永远都学不会这个算法,最好的方法就是从一些简单的例题着手,一点一点去按照自己的方式理解,而不是背概念。

对于动态规划问题,最简单的就是线性动态规划,这堂课我们就利用一些,非常经典的线性动态规划问题来进行分析,从而逐个击破。

二、常见问题

1、爬楼梯

  1. 问题描述:有一个 n 级楼梯,每次可以爬 1 或者 2 级。问有多少种不同的方法可以爬到第 n 级。
  2. 状态:dp[i] 表示爬到第 i 级楼梯的方案数。
  3. 初始状态:dp[0] = dp[1] = 1
  4. 状态转移方程:dp[i] = dp[i-1] + dp[i-2]。 (对于爬到第 i 级,可以从 i-1 级楼梯爬过来,也可以从 i-2 级楼梯爬过来)
  5. 状态数:O(n)
  6. 状态转移消耗:O(1)
  7. 时间复杂度:O(n)
    class Solution {
    public:int climbStairs(int n) {vector<int> dp(n+1);dp[0] = dp[1] = 1;for (int i = 2; i < dp.size(); i++)dp[i] = dp[i - 1] + dp[i - 2];return dp[n];}
    };

2、最大子数组和(最大子段和)

  1. 问题描述:给定一个 n 个元素的数组 arr[],求一个子数组,并且它的元素和最大,返回最大的和。
  2. 状态:dp[i] 表示以第 i 个元素结尾的最大子数组和。
  3. 初始状态:dp[0] = arr[0](可以为负数)
  4. 状态转移方程:dp[i] = arr[i] + max(dp[i-1], 0)。 (因为是以第i个元素结尾,所以 arr[i]必选, dp[i-1] 这部分是以第 i-1 个元素结尾的,可以不选或者选,完全取决于它是否大于0,所以选和不选取大者)
  5. 状态数:O(n)
  6. 状态转移消耗:O(1)
  7. 时间复杂度:O(n)
class Solution {
public:int maxSubArray(vector<int>& arr) {vector<int> dp(arr.size()+1);dp[0] = arr[0];int maxSum=dp[0];for(int i=1;i<arr.size();i++){dp[i] = arr[i] + max(dp[i-1], 0);maxSum = max(maxSum, dp[i]);}return maxSum;}
};还有一个双O(1)的方法
class Solution {
public:int maxSubArray(vector<int>& arr) {if (arr.empty()) return 0;int currentSum = arr[0];int maxSum = arr[0];for (int i = 1; i < arr.size(); ++i) {currentSum = max(currentSum + arr[i], arr[i]);maxSum = max(maxSum, currentSum);}return maxSum;}
};

3、最长递增子序列

  1. 问题描述:给定一个 n 个元素的数组 arr[],求一个最大的子序列的长度,序列中元素单调递增。
  2. 状态:dp[i] 表示以第 i 个元素结尾的最长递增子序列的长度。
  3. 初始状态:dp[0] = 1
  4. 状态转移方程:dp[i] = max(dp[i], dp[j] + 1)。(arr[j] < arr[i]) (对于所有下标比 i 小的下标 j,并且满足 arr[j] < arr[i] 的情况,取所有这里面 dp[j] 的最大值 加上 1 就是 dp[i] 的值,当然可能不存在这样的 j,那么这时候 dp[i] 的值就是 1)
  5. 状态数:O(n)
  6. 状态转移消耗:O(n)
  7. 时间复杂度:O(n^2)
class Solution {
public:int lengthOfLIS(vector<int>& nums) {vector<int> dp(nums.size(), 1);int maxlength = 1;for (int i = 1; i < nums.size(); i++) {for (int j = 0; j < i; j++) {if (nums[j] < nums[i]) {dp[i] = max(dp[i], dp[j] + 1);maxlength = max(maxlength, dp[i]);}}}return maxlength;}
};

4、数字三角形

  1. 问题描述:给定一个 n 行的三角形 triangle[][],找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1。
  2. 状态:dp[i][j] 表示从顶部走到 (i, j) 位置的最小路径和。
  3. 初始状态:dp[0][0] = triangle[0][0];起点就是顶部,路径和只能是它自己。
  4. 状态转移方程:dp[i][j] = max(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j]。(走到 (i,j) 的路径只能从两个方向来:从左上方来(即从 (i-1, j-1) 走到 (i,j))从上方来(即从 (i-1, j) 走到 (i,j))所以我们只需要比较这两个方向的最小值,加上当前位置的值即可。)
  5. 状态数:O(n^2)
  6. 状态转移消耗:O(1)
  7. 时间复杂度:O(n^2)

class Solution {
public:int minimumTotal(vector<vector<int>>& triangle) {int n = triangle.size();vector<vector<int>> dp(n, vector<int>(n, 0));int minsum = 0;dp[0][0] = triangle[0][0];for (int i = 1; i < triangle.size(); i++) {for (int j = 0; j < triangle[i].size(); j++) {if (j == 0)dp[i][j] = dp[i - 1][j] + triangle[i][j];else if (j == i)dp[i][j] = dp[i - 1][j - 1] + triangle[i][j];elsedp[i][j] =min(dp[i - 1][j - 1], dp[i - 1][j]) + triangle[i][j];}}return *min_element(dp[n - 1].begin(), dp[n - 1].end());}
};

5、股票系列

力扣有一些非常经典的股票问题,可以自己尝试去看一下。 

121. 买卖股票的最佳时机这个解法不是dp

class Solution {
public:int maxProfit(vector<int>& prices) {int cost = INT_MAX, profit = 0;for (int price : prices) {cost = min(cost, price);profit = max(profit, price - cost);}return profit;}
};

122. 买卖股票的最佳时机 II

 你需要在每一天决定是否 买、卖、或不操作 股票,最终获得 最大利润 。
限制条件: 任何时候最多只能持有一股股票(即必须先卖出才能再买)。

我们每天的状态只有两种可能:

  • 状态 0: 手里没有股票(可以买)
  • 状态 1: 手里有股票(可以卖)

我们用一个二维数组 dp[i][0]dp[i][1] 来记录第 i 天结束后,这两种状态下的 最大利润

从第 i-1 天的状态推导第 i 天的状态 

(1) 当前状态 0(手里没有股票)
  • 可能来源:
    • 昨天也没股票(今天没买),利润不变:dp[i-1][0]
    • 昨天有股票(今天卖了),利润 = 昨天有股票的利润 + 今天卖出的价格:dp[i-1][1] + prices[i]
  • 取最大值:dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
(2) 当前状态 1(手里有股票)
  • 可能来源:
    • 昨天也有股票(今天没卖),利润不变:dp[i-1][1]
    • 昨天没股票(今天买了),利润 = 昨天没股票的利润 - 今天买入的价格:dp[i-1][0] - prices[i]
  • 取最大值:dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])

4. 初始状态

  • 第 0 天(第一天):
    • dp[0][0] = 0(没有买,利润为 0)
    • dp[0][1] = -prices[0](买了,但还没卖,利润为负)

5. 最终答案

最后一天 不持有股票 的利润一定是最大的(因为持有股票还没卖的话,利润可能不是最大):return dp[n-1][0] // n 是天数

class Solution {
public:int maxProfit(vector<int>& prices) {int n = prices.size();vector<vector<int>> dp(n, vector<int>(2, 0));// 初始状态dp[0][0] = 0;dp[0][1] = -prices[0];for (int i = 1; i < n; ++i) {dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);}return dp[n-1][0];  // 返回最后一天不持有股票的最大利润}
};

123. 买卖股票的最佳时机 III

class Solution {
public:int maxProfit(vector<int>& prices) {int n = prices.size();vector<vector<int>> dp(n, vector<int>(4));dp[0][0] = -prices[0], dp[0][1] = 0, dp[0][2] = -prices[0],dp[0][3] = 0;for (int i = 1; i < n; i++) {dp[i][0] = max(-prices[i], dp[i - 1][0]);dp[i][1] = max(dp[i - 1][0] + prices[i], dp[i - 1][1]);dp[i][2] = max(dp[i - 1][1] - prices[i], dp[i - 1][2]);dp[i][3] = max(dp[i - 1][2] + prices[i], dp[i - 1][3]);}return max(dp[n - 1][1], dp[n - 1][3]);}
};

188. 买卖股票的最佳时机 IV

fucking-algorithm/动态规划系列/团灭股票问题.md at master · labuladong/fucking-algorithm

309. 买卖股票的最佳时机含冷冻期

714. 买卖股票的最佳时机含手续费

6、最短路径问题

Dijkstra 本质也是一个动态规划问题。只不过通过不断更新状态,来实现状态转移。

7、背包问题

0/1背包DP

完全背包DP

三、细节剖析

1、问题求什么,状态就尽量定义成什么,有了状态,再去尽力套状态转移方程。

2、动态规划的时间复杂度等于 状态数 x 状态转移 的消耗;

3、状态转移方程中的 i 变量导致数组下标越界,从而可以确定哪些状态是初始状态;

4、状态转移的过程一定是单向的,把每个状态理解成一个结点,状态转移理解成边,动态规划的求解就是在一个有向无环图上进行递推计算。

5、因为动态规划的状态图是一个有向无环图,所以一般会和拓扑排序联系起来。

 题目

接龙数列

数组切分

最大魅力值

0、自然语言视频题解

  1. 接龙数列
  2. 数组切分
  3. 最大魅力值

3、C++视频题解

  1. 接龙数列
  2. 数组切分
  3. 最大魅力值

  使用最小花费爬楼梯

  打家劫舍

  删除并获得点数

  买卖股票的最佳时机(带字幕版)

递推

斐波那契数

第 N 个泰波那契数

剑指 Offer 10- II. 青蛙跳台阶问题

三步问题

剑指 Offer 10- I. 斐波那契数列

爬楼梯

剑指 Offer II 003. 前 n 个数字二进制中 1 的个数

旋转函数

访问完所有房间的第一天

线性DP / 状态转移 O(C)

使用最小花费爬楼梯

剑指 Offer II 088. 爬楼梯的最少成本

解决智力问题

打家劫舍

剑指 Offer II 089. 房屋偷盗

按摩师

打家劫舍 II

剑指 Offer II 090. 环形房屋偷盗

剑指 Offer 46. 把数字翻译成字符串

解码方法

1 比特与 2 比特字符

使序列递增的最小交换次数

恢复数组

秋叶收藏集

删除并获得点数

完成比赛的最少时间

线性DP / 状态转移 O(n)

单词拆分

分隔数组以得到最大和

最低票价

跳跃游戏 II

带因子的二叉树

最大子数组和

剑指 Offer 42. 连续子数组的最大和

连续数列

最大子数组和

任意子数组和的绝对值的最大值

乘积最大子数组

乘积为正数的最长子数组长度

删除一次得到子数组最大和

最长递增子序列

最长数对链

最长递增子序列的个数

摆动序列

最长湍流子数组

最长递增子序列

最长字符串链

堆箱子

俄罗斯套娃信封问题

马戏团人塔

使数组 K 递增的最少操作次数

股票问题

股票平滑下跌阶段的数目

买卖股票的最佳时机 II

买卖股票的最佳时机含手续费

最佳买卖股票时机含冷冻期

买卖股票的最佳时机 III

前缀最值

有效的山脉数组

将每个元素替换为右侧最大元素

买卖股票的最佳时机

最佳观光组合

数组中的最长山脉

适合打劫银行的日子

两个最好的不重叠活动

接雨水

移除所有载有违禁货物车厢所需的最少时间

接雨水 II

前缀和

分割字符串的最大得分

哪种连续子字符串更长

翻转字符

将字符串翻转到单调递增

删掉一个元素以后全为 1 的最长子数组

和为奇数的子数组数目

两个非重叠子数组的最大和

K 次串联后最大子数组之和

找两个和为目标值且不重叠的子数组

生成平衡数组的方案数

三个无重叠子数组的最大和

统计特殊子序列的数目

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

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

相关文章

MySQL中sql_mode的设置

■ 57版本原来配置 show variables like %sql_mode%; STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION ■ 修改配置文件 注释掉sql_mode&#xff0c;并重启&#xff0c;查看57版本的默认设置 ONL…

MCAL学习(1)——AutoSAR

1.了解AutoSAR及一些概念 AutoSAR是Automotive Open System Architecture ,汽车开放系统架构。 针对汽车ECU的软件开发架构。已经是汽车电子软件开发的标准。 OS服务&#xff1a;Freertos 整车厂&#xff08;OEM&#xff09;主要负责应用层算法 一级供应商&#xff1a;生产制…

Vue报错:Cannot read properties of null (reading ‘xxx‘)

一、报错问题 Cannot read properties of null (reading style)at patchStyle (runtime-dom.esm-bundler.js:104:22)二、错误排查 这类报错一般是在已经开发好后&#xff0c;后面测试时突然发现的&#xff0c;所以不好排查错误原因。 三、可能原因及解决方案 v-if 导致 在 …

25G 80km双纤BIDI光模块:远距传输的创新标杆

目录 一、产品优势&#xff1a;双纤与BIDI的独特价值 易天光通信25G SFP28 ZR 80KM 易天光通信25G SFP28 BIDI ZR 80KM 二、权威认证与技术突破 三、双纤与BIDI的核心差异解析 四、应用场景&#xff1a;驱动多领域高效互联 总结 在5G、云计算与数字化转型的推动下&#xff0c;光…

2025-05-06 学习记录--Python-注释 + 打印变量 + input输入

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、注释 ⭐️ &#xff08;一&#xff09;、块注释 &#x1f36d; 举例&#xff1a; &#x1f330; # 打印数字 print(2025) …

基于mediapipe深度学习的眨眼检测和计数系统python源码

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 人工智能算法python程序运行环境安装步骤整理_本地ai 运行 python-CSDN博客 3.部分核心程序 &…

怎样通过API 实现python调用Chatgpt,gemini

怎样通过API 实现python调用Chatgpt,gemini 以下为你详细介绍如何设置和调用这些参数,以创建一个类似的 ChatCompletion 请求: 1. 安装依赖库 如果你使用的是 OpenAI 的 API 客户端,需要先安装 openai 库。可以使用以下命令进行安装: pip install openai2. 代码示例 …

Linux 下MySql主从数据库的环境搭建

测试环境&#xff1a;两台服务器&#xff0c;Mysql版本 8.0&#xff0c;linux版本&#xff1a;Ubuntu 20.04.3&#xff1b; 1.在两台服务器上安装MySql&#xff1b; 2.选一台作为主服务器&#xff0c;在主服务器上以root用户进入Mysql&#xff0c;执行以下语句&#xff1a; …

力扣1812题解

记录 2025.5.7 题目&#xff1a; 思路&#xff1a; 从左下角开始&#xff0c;棋盘的行数和列数&#xff08;均从 1 开始计数&#xff09;之和如果为奇数&#xff0c;则为白色格子&#xff0c;如果和为偶数&#xff0c;则为黑色格子。 代码&#xff1a; class Solution {pu…

适合java程序员的Kafka消息中间件实战

创作的初心&#xff1a; 我们在学习kafka时&#xff0c;都是基于大数据的开发而进行的讲解&#xff0c;这篇文章为java程序员为核心&#xff0c;助力大家掌握kafka实现。 什么是kafka: 历史&#xff1a; 诞生与开源&#xff08;2010 - 2011 年&#xff09; 2010 年&#xf…

PDF智能解析与知识挖掘:基于pdfminer.six的全栈实现

前言 在数字化信息爆炸的时代&#xff0c;PDF&#xff08;便携式文档格式&#xff09;作为一种通用的电子文档标准&#xff0c;承载着海量的结构化与非结构化知识。然而&#xff0c;PDF格式的设计初衷是用于展示而非数据提取&#xff0c;这使得从PDF中挖掘有价值的信息成为数据…

Python爬虫+代理IP+Header伪装:高效采集亚马逊数据

1. 引言 在当今大数据时代&#xff0c;电商平台&#xff08;如亚马逊&#xff09;的数据采集对于市场分析、竞品监控和价格追踪至关重要。然而&#xff0c;亚马逊具有严格的反爬虫机制&#xff0c;包括IP封禁、Header检测、验证码挑战等。 为了高效且稳定地采集亚马逊数据&am…

架构思维:探讨架构师的本质使命

文章目录 软件工程1. 软件工程的定义与核心目标2. 软件工程 vs. 软件项目管理3. 软件工程的两大特性4. 软件工程的关键活动与方法论5. 架构师在软件工程中的职责架构师的职责和思维架构师心性修炼三大核心能力架构设计的基本准则 团队共识“设计文档”的统一结构框架阅读他人代…

QT设计权限管理系统

Qt能够简单实现系统的权限设计 首先我们需要一个登陆界面 例如这样 然后一级权限&#xff0c;可以看到所有的内容&#xff0c;不设置菜单栏的隐藏。 然后其他权限&#xff0c;根据登陆者的身份进行菜单栏不同的展示。 菜单栏的隐藏代码如下&#xff1a; ui->actionuser-…

Debezium 架构详解与实战示例

Debezium 架构详解与实战示例 1. 整体架构图 #mermaid-svg-tkAquOxA2pylXzON {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-tkAquOxA2pylXzON .error-icon{fill:#552222;}#mermaid-svg-tkAquOxA2pylXzON .error-t…

Qt天气预报系统更新UI界面

Qt天气预报系统更新UI界面 1、创建各个小部分列表2、定义一个更新UI函数2.1 实现更新UI界面函数 1、创建各个小部分列表 QList<QLabel *> weekList; //星期 QList<QLabel *> dateList; //日期QList<QLabel *> weatherL…

AWS MCP Servers

文章目录 一、关于 AWS MCP Servers什么是模型上下文协议&#xff08;MCP&#xff09;以及它是如何与AWS MCP服务器协同工作的&#xff1f;为什么选择MCP服务器&#xff1f; 二、可用 Servers核心MCP服务器AWS 文档服务器亚马逊 Bedrock 知识库检索 MCP 服务器AWS CDK MCP 服务…

python如何把pdf转word

在Python中将PDF转换为Word文档&#xff08;.docx&#xff09;比反向转换&#xff08;Word转PDF&#xff09;更具挑战性&#xff0c;因为PDF是固定格式&#xff0c;而Word是可编辑格式。以下是几种可行的方法及详细步骤&#xff1a; 方法1&#xff1a;使用 pdf2docx 库 pdf2do…

NLP 和大模型技术路线

transformers快速入门 NLP 和大模型技术路线 在自然语言处理&#xff08;NLP&#xff09;和大模型领域&#xff0c;技术路线的学习应该从基础开始&#xff0c;逐步深入到更高阶的应用和优化技术。本文将详细介绍相关技术点的学习顺序&#xff0c;以及每个技术点的关键学习内容…

WordPress个人博客搭建(二):在 Ubuntu 22.04 x64 系统中使用1Panel 部署 WordPress

前言 在之前的安装1Panel面板的文章中&#xff0c;我们已经成功将1Panel面板安装到了2核4G配置的非凡云云服务器上。1Panel作为一款现代化的服务器管理面板&#xff0c;极大简化了网站部署流程。本文将详细介绍如何使用1Panel面板在云服务器上安装部署WordPress&#xff0c;帮…