Leetcode动态规划模板

0 前言

路径基本要素说明
核心基础穷举法需“聪明”穷举
存在问题重叠子问题有众多相同子问题(eg.多个f(18)),需记录
具备特点最优子结构原问题的解包含子问题的解
状态转移思维模式自顶向下(推荐)当前状态如何由前一个状态推出

目前所遇动态规划问题一般形式包括

形式状态转移方程一般式(滚动数组)初始化遍历顺序
求最值 dp[j] = max(dp[j], dp[j - weight[i]] + val[i])dp[0] = 0物品和背包可颠倒
但先物品后容量耗时低
求组合数dp[j] = dp[j] + dp[j - weight[i]]dp[0] = 1先物品,后背包容量
求排列数 dp[j] = dp[j] + dp[j - weight[i]]dp[0] = 1先背包容量,后物品
  • 这里为什么变动一下遍历顺序,就从求组合数变成求排列数了呢?因为先遍历物品后遍历背包容量,隐含了我在《Leetcode回溯法四板一解模板》中提到的对待组合问题的first索引技巧,不用它则求解为排列问题(不得不说动态规划和回溯法面对同类型题,特定领域技巧是相通的)详情可以点击了解.
  • 为什么在不要求遍历顺序的问题上,更推荐先遍历物品,再遍历背包容量呢?因为组合比排列有更少的搜索量!
背包集合代码特点原因for外遍历顺序
0-1背包背包容量for循环j--降序不论容量多少最多装1次,避免累加组合→先物品后背包
排列→先背包后物品
完全背包背包容量for循环j++升序容量越多最多可方便无限累加组合→先物品后背包
排列→先背包后物品

1 解题思考模式

1.1 能不能用动态规划做?

由于面试解题没有时间进行数学验证,因此需要训练一些基本feeling,满足feeling即可果断尝试,十拿九稳!

  • if能穷举出所有解,回溯法能不能搜索出所有解if是, then回溯可得正确答案
  • 原问题是否包含多个相同子问题if是, then回溯可能会超时 (力扣:编辑距离)
  • 子问题的解是否相互独立?,最起码独立满足最优子结构性质
    ——独立:总高考分 = 语文成绩 + 数学成绩 + …,其中语文成绩与数学成绩无关
    ——相关:总高考分 = 语文成绩 + 数学成绩 + …,其中语文成绩与数学成绩成反比关系,导致无法同时最优
  • 满足以上条件可用动态规划做

1.2 怎么用动态规划做?(七步走)

顺序思考步骤解释/例子
1有什么状态容量,累计和,金额等
2有什么选择该物品装与不装,该数值是否累计,该硬币是否添加等
3dp[j]数组含义是什么?索引j状态(eg.容量),返回值dp[j]是题目的解
4如何定义状态转移任意状态前状态经过怎样的选择转移达到(自顶向下)
5如何初始化dp数组结合状态转移初始化,eg.max初始全0,求和dp[0]=1,有负数初始INT_MIN
6for遍历顺序求最值推荐先物品后背包,求组合先物品后背包,求排列先背包后物品
7for遍历顺序有状态压缩背包容量for内降序无状态压缩升序

2 动态规划模板

2.1 通用模板

// 1.通用初始化
vector<int> dp(容量 + 1, base case1);
// 2.边界初始化
dp[0][0][...] = base case2
// 3.状态转移
for 状态1的个数
	for 状态2的个数
		for ...
			dp[状态1][状态2][...] = 求最值 or 求和(选择1, 选择2...)

2.2 背包模板

在这里插入图片描述
以下模板均以状态压缩后的滚动数组为例

2.2.1 01背包模板

每个物品有装1次,或不装两个选择

[1] 最值问题

状态转移方程:背包容量下的最值 = max(不装物品i最值, 装物品i的最值),最小反之

  • 物品价值全部非负
// 1.通用dp初始化
vector<int> dp(背包容量 + 1, 0);
for (int i = 0; i < 物品总数; i++)
    for (int j = 背包容量; j >= 物品i的重量; j--) {
        // 3.背包容量下的最值 = max(不装物品i最值, 装物品i的最值)
        dp[j] = max(dp[j], dp[j - 物品i的重量] + 物品i的价值);
    }
return dp[背包容量];
  • 物品价值存在负数 (初始化INT_MININT_MAX + for内判断)
// 1.通用dp初始化
vector<int> dp(背包容量 + 1, INT_MIN);
// 2.边界dp初始化
dp[0] = 0; 
for (int i = 0; i < 物品总数; i++)
    for (int j = 背包容量; j >= 物品i的重量; j--) {
    	if (dp[i - 物品i的重量] == INT_MIN) continue;   // 因为不能有INT_MIN + 常数
        // 3.背包容量下的最值 = max(不装物品i最值, 装物品i的最值)
        dp[j] = max(dp[j], dp[j - 物品i的重量] + 物品i的价值);
    }
return dp[背包容量];

[2] 组合问题

  • 返回具体组合数

状态转移方程:总组合数 = 不装物品i的组合数 + 装物品i的组合数

// 1.通用dp初始化
vector<int> dp(背包容量 + 1, 0);
// 2.边界dp初始化
dp[0] = 1;    // [关键]:根据状态转移方程,dp[0]须为1
for (int i = 0; i < 物品总数; i++)
    for (int j = 背包容量; j >= 物品i的重量; j--) {
        // 3.总组合数 = 不装物品i的组合数 + 装物品i的组合数
        dp[j] = dp[j] + dp[j - 物品i的重量];
    }
return dp[背包容量];
  • 返回组合的存在还是不存在

状态转移方程:组合是否有 = 不装物品i的组合是否有 || 装物品i的组合是否有

// 1.通用dp初始化
vector<int> dp(背包容量 + 1, false);
// 2.边界dp初始化
dp[0] = true;    // [关键]:根据状态转移方程,dp[0]须为true
for (int i = 0; i < 物品总数; i++)
    for (int j = 背包容量; j >= 物品i的重量; j--) {
        // 3.组合是否有 = 不装物品i的组合是否有 || 装物品i的组合是否有
        dp[j] = dp[j] || dp[j - 物品i的重量];
    }
return dp[背包容量];

[3] 排列问题

状态转移方程:总排列数 = 不装物品i的排列数 + 装物品i的排列数

// 1.通用dp初始化
vector<int> dp(背包容量 + 1, 0);
// 2.边界dp初始化
dp[0] = 1;    // [关键]:根据状态转移方程,dp[0]须为1
for (int j = 1; j <= 背包容量; j++) {
    for (int i = 0; i < 物品总数; i++)
        // 3.总组合数 = 不装物品i的排列数  + 装物品i的排列数 
        dp[j] = dp[j] + dp[j - 物品i的重量];
    }
return dp[背包容量];

2.2.2 完全背包模板

每个物品有装无限次,或不装两个选择

组合问题(背包容量for循环升序)

  • 返回具体组合数

状态转移方程:总组合数 = 不装物品i的组合数 + 装物品i的组合数

// 1.通用dp初始化
vector<int> dp(背包容量 + 1, 0);
// 2.边界dp初始化
dp[0] = 1;    // [关键]:根据状态转移方程,dp[0]须为1
for (int i = 0; i < 物品总数; i++)
	for (int j = 物品i的重量; j <= 背包容量; j++) {
        // 3.总组合数 = 不装物品i的组合数 + 装物品i的组合数
        dp[j] = dp[j] = dp[j] + dp[j - 物品i的重量]; 
    }
return dp[背包容量];

刷题还在继续,持续总结中~

致谢

以上总结为个人原创,但图片来源于「代码随想录」公众号,欢迎大家关注这位大佬的公号

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
1. 二分法 5 1.1. 什么是二分查找 5 1.2. 如何识别二分法 5 1.3. 二分法模板 6 1.3.1. 模板一 6 1.3.1.1. 模板代码 6 1.3.1.2. 关键属性 7 1.3.1.3. 语法说明 7 1.3.1.4. Lc69:x的平方根 8 1.3.1.5. Lc374:猜数大小 9 1.3.1.6. Lc33:搜索旋转数组 11 1.3.2. 模板二 13 1.3.2.1. 模板代码 13 1.3.2.2. 关键属性 14 1.3.2.3. 语法说明 14 1.3.2.4. Lc278:第一个错误版本 14 1.3.2.5. Lc162:寻找峰值 16 1.3.2.6. Lc153:寻找旋转排序数组最小值 19 1.3.2.7. Lc154:寻找旋转排序数组最小值II 20 1.3.3. 模板三 22 1.3.3.1. 模板代码 22 1.3.3.2. 关键属性 23 1.3.3.3. 语法说明 23 1.3.3.4. LC-34:在排序数组中查找元素的第一个和最后一个 23 1.3.3.5. LC-658:找到K个最接近的元素 25 1.3.4. 小结 28 1.4. LeetCode中二分查找题目 29 2. 双指针 30 2.1. 快慢指针 31 2.1.1. 什么是快慢指针 31 2.1.2. 快慢指针模板 31 2.1.3. 快慢指针相关题目 32 2.1.3.1. LC-141:链表是否有环 32 2.1.3.2. LC-142:环形链表入口 34 2.1.3.3. LC-876:链表的中间节点 37 2.1.3.4. LC-287:寻找重复数 40 2.2. 滑动窗口 43 2.2.1. 什么是滑动窗口 43 2.1.4. 常见题型 44 2.1.5. 注意事项 45 2.1.6. 滑动窗口模板 45 2.1.7. 滑动窗口相关题目 46 2.1.7.1. LC-3:无重复字符的最长子串 47 2.1.7.2. LC-76:最小覆盖子串 49 2.1.7.3. LC-209:长度最小的子数组 54 2.1.7.4. LC-239:滑动窗口最大值 57 2.1.7.5. LC-395:至少有K个重复字符的最长子串 60 2.1.7.6. LC-567:字符串排列 62 2.1.7.7. LC-904:水果成篮 64 2.1.7.8. LC-424:替换后的最长重复字符 66 2.1.7.9. LC-713:乘积小于K的子数组 67 2.1.7.10. LC-992:K个不同整数的子数组 70 2.3. 左右指针 73 2.3.1. 模板 73 2.3.2. 相关题目 73 2.3.2.1. LC-76:删除倒数第N个节点 74 2.3.2.2. LC-61:旋转链表 76 2.3.2.3. LC-80:删除有序数组中的重复项 79 2.3.2.4. LC-86:分割链表 80 2.3.2.5. LC-438:找到字符串中所有字母的异位词 82 3. 模板 85 2.3.2.6. LC-76:删除倒数第N个节点 85
插件名字叫leetcode editor,可以解决在上班时想刷leetcode但又不想直接打开leetcode界面太扎眼或者无法debug的问题。你可以在IDEA的plugins中搜索并下载leetcode editor插件。 插件的下载地址是https://plugins.jetbrains.com/plugin/12132-leetcode-editor。 下载并安装插件后,你可以在IDEA的File -> Settings -> Tools -> Leetcode Plugin***com作为网址选项。此外,你还可以选择代码类型,包括Java、Python、C、Python3、C、C#、JavaScript、Ruby、Swift、Go、Scala、Kotlin、Rust、PHP。你需要输入登录名和密码来登录leetcode账号,并可以设置临时文件的存放目录和HTTP Proxy。 如果你想自定义代码模板,可以参考该插件提供的自定义代码模板文档(https://github.com/shuzijun/leetcode-editor/blob/master/doc/CustomCode.md)。通过这个插件,你可以方便地在IDEA中刷leetcode,并享受更好的调试体验。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [好用的idea插件leetcode editor【详细安装指南】](https://blog.csdn.net/weixin_45988401/article/details/129170239)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [IDEA插件系列(112):LeetCode Editor插件——LeetCode编辑器](https://blog.csdn.net/cnds123321/article/details/119859448)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SL_World

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值