从小白开始的动态规划

embedded/2025/2/13 0:08:56/

一、动态规划的核心思想

动态规划(DP)通过拆分问题+记忆化计算解决复杂问题,核心步骤为:

  1. 定义状态:用变量(如dp[i])表示子问题的解

  2. 状态转移方程:建立子问题之间的关系式

  3. 初始化:确定基础情况的初始值

  4. 计算顺序:确定填表方向(自底向上/自顶向下)

二、动态规划解题四部曲

  1. 分析问题是否具有重叠子问题最优子结构

  2. 定义明确的状态表示

  3. 推导状态转移关系

  4. 处理边界条件并实现


三、经典DP问题分类与实战

类型1:记忆化递归(斐波那契数列)

问题:计算第n个斐波那契数
分析

  • 状态定义:dp[i]表示第i个斐波那契数

  • 转移方程:dp[i] = dp[i-1] + dp[i-2]

int fib(int n) {if(n <= 1) return n;vector<int> dp(n+1);dp[0] = 0; dp[1] = 1;for(int i=2; i<=n; ++i){dp[i] = dp[i-1] + dp[i-2];}return dp[n];
}
类型2:线性DP(爬楼梯)

问题:每次爬1/2阶,到n阶有多少种方法
分析

  • 状态定义:dp[i]表示到达第i阶的方法数

  • 转移方程:dp[i] = dp[i-1] + dp[i-2]

int climbStairs(int n) {if(n <= 2) return n;vector<int> dp(n+1);dp[1] = 1; dp[2] = 2;for(int i=3; i<=n; ++i){dp[i] = dp[i-1] + dp[i-2];}return dp[n];
}
类型3:状态机DP(打家劫舍)

问题:不能偷相邻房屋,求最大收益
分析

  • 状态定义:dp[i][0]不偷第i家的最大收益,dp[i][1]偷第i家的收益

  • 转移方程:
    dp[i][0] = max(dp[i-1][0], dp[i-1][1])
    dp[i][1] = dp[i-1][0] + nums[i]

int rob(vector<int>& nums) {int n = nums.size();vector<vector<int>> dp(n, vector<int>(2));dp[0][0] = 0; dp[0][1] = nums[0];for(int i=1; i<n; ++i){dp[i][0] = max(dp[i-1][0], dp[i-1][1]);dp[i][1] = dp[i-1][0] + nums[i];}return max(dp[n-1][0], dp[n-1][1]);
}
类型4:背包问题(0-1背包)

问题:容量W的背包,物品重量weight[]和价值value[],求最大价值
分析

  • 状态定义:dp[i][j]前i个物品装入容量j的背包的最大价值

  • 转移方程:
    dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])

int knapsack(int W, vector<int>& weight, vector<int>& value) {int n = weight.size();vector<vector<int>> dp(n+1, vector<int>(W+1));for(int i=1; i<=n; ++i){for(int j=1; j<=W; ++j){if(j >= weight[i-1]){dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i-1]] + value[i-1]);}else{dp[i][j] = dp[i-1][j];}}}return dp[n][W];
}
类型5:二维路径DP(最小路径和)

问题:m x n网格,从左上到右下的最小路径和
分析

  • 状态定义:dp[i][j]表示到(i,j)的最小路径和

  • 转移方程:
    dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]

int minPathSum(vector<vector<int>>& grid) {int m = grid.size(), n = grid[0].size();vector<vector<int>> dp(m, vector<int>(n));dp[0][0] = grid[0][0];// 初始化第一列for(int i=1; i<m; ++i) dp[i][0] = dp[i-1][0] + grid[i][0];// 初始化第一行for(int j=1; j<n; ++j) dp[0][j] = dp[0][j-1] + grid[0][j];for(int i=1; i<m; ++i){for(int j=1; j<n; ++j){dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];}}return dp[m-1][n-1];
}
类型6:字符串DP(编辑距离)

问题:将word1转换为word2的最小操作次数(增/删/改)
分析

  • 状态定义:dp[i][j]表示word1前i个字符转成word2前j个字符的最小步数

  • 转移方程:

if(word1[i-1] == word2[j-1]) dp[i][j] = dp[i-1][j-1];
elsedp[i][j] = min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]}) + 1;
int minDistance(string word1, string word2) {int m = word1.size(), n = word2.size();vector<vector<int>> dp(m+1, vector<int>(n+1));for(int i=0; i<=m; ++i) dp[i][0] = i;for(int j=0; j<=n; ++j) dp[0][j] = j;for(int i=1; i<=m; ++i){for(int j=1; j<=n; ++j){if(word1[i-1] == word2[j-1]){dp[i][j] = dp[i-1][j-1];}else{dp[i][j] = min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]}) + 1;}}}return dp[m][n];
}

四、动态规划的优化技巧

  1. 状态压缩:二维转一维(如背包问题使用滚动数组)

  2. 空间优化:复用数组空间

  3. 记忆化搜索:递归+备忘录的写法


五、如何有效练习DP

  1. 从经典模板题入手(斐波那契、背包、路径问题)

  2. 先尝试自己推导状态转移方程

  3. 对比他人解法优化空间复杂度

  4. 逐步挑战Hard题目(最长递增子序列、股票买卖问题等)

动态规划就像搭积木——找到正确的子问题拆分方式,建立状态间的联系,通过不断练习积累"问题模式"的识别能力。坚持每天解决一个DP问题,你会在两个月后发现自己质的飞跃!


http://www.ppmy.cn/embedded/161722.html

相关文章

ASP.NET Core SignalR的协议协商

SignalR支持多种服务器推送方式&#xff1a;Websocket、Server-Sent Events、长轮询。默认按顺序尝试。F12查看协商过程。websocket和HTTP是不同的协议&#xff0c;为什么能用同一个端口。在【开发人员工具】的【网络】页签中看WebSocket通信过程。 协议协商问题 集群中协议协…

关于SoC产品介绍:ICNM8001

这个是一款关于QHD显示器&#xff0c;主控芯片scaler IC。 功能&#xff1a;支持2路HDMI2.0接收机 支持HDCP1.4和HDCP2.22&#xff1b;支持HDCP1.4和HDCP2.2。 分辨率&#xff1a;2560*1440 刷新率&#xff1a;75Hz HDR&#xff1a;HDR10 封装&#xff1a;QFP216 接口&#xf…

如何在WPF中实现软件内嵌效果

1.创建Process进程&#xff0c;设置运行程序文件路径 Process proc new Process(); proc.StartInfo.FileName "C:\Users\hdevelop.exe"; proc.Start(); proc.WaitForInputIdle(); 2.根据创建的进程获取窗口句柄 IntPtr hWnd proc.MainWindowHandle; 3.开启线程…

antd-react日期组件disabledDate不可选择的日期 (置灰)属性

需求&#xff1a;原定结项时间表单里回显为2025-02-06&#xff0c;延期时间的选择范围要设置从2025-02-07开始选择包括2.7号的; 2.7号之前的置灰&#xff0c;不可选择 PC端部分代码&#xff1a; // react的函数组件写法 const disabledDate function (current) {console.log(c…

CSS 小技巧 —— CSS 实现 Tooltip 功能-鼠标 hover 之后出现弹层

CSS 小技巧 —— CSS 实现 Tooltip 功能-鼠标 hover 之后出现弹层 1. 两个元素实现 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>纯 CSS 实现 Tooltip 功能-鼠标 hover 之后出现弹层</titl…

npm运行Vue项目报错 error:0308010c:digital envelope routines::unsupported

大家好&#xff0c;我是 程序员码递夫。 问题 VSCode 运行Vue项目&#xff0c;提示错误&#xff1a; building 2/2 modules 0 activeError: error:0308010c:digital envelope routines::unsupported 解决方法 原因是 npm 高版本(大于17)&#xff0c;对ssl的处理做了改进&…

Windows命令行学习(和Linux命令比对记忆

本文侧重于记忆Windows简单命令&#xff0c;所以相关Linux命令演示较少&#xff0c;学习Linux命令详见Linux系统专栏&#xff1b;本文示例版本为Windows 10与Linux 7。 目录 一、文件与目录操作 1、列出当前目录下文件及子目录信息 Windows&#xff1a;dir Linux&#xff1…

SpringBoot单机模式,能否支持一万用户请求并发?

Spring Boot 单机模式能否支持一万用户请求并发&#xff0c;取决于多个因素&#xff1a; 硬件配置&#xff1a;CPU、内存、磁盘I/O和网络带宽是关键。高性能硬件能显著提升并发处理能力。 应用复杂度&#xff1a;业务逻辑复杂度和数据库操作频率会影响性能。复杂的业务逻辑和高…