前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >算法-动态规划

算法-动态规划

作者头像
宅蓝三木
发布于 2024-10-09 13:17:35
发布于 2024-10-09 13:17:35
16700
代码可运行
举报
文章被收录于专栏:三木的博客三木的博客
运行总次数:0
代码可运行

动态规划是一种解决多阶段决策过程最优化问题的数学方法。通常需要保存决策路径的问题用回溯法,而只是求最优解的时候选择动态规划。

基本概念

  • 定义:动态规划通过把原问题分解为相对简单的子问题,并保存子问题的解,避免重复计算,从而高效地求解复杂问题。它通常适用于具有最优子结构和子问题重叠性质的问题。
  • 最优子结构:一个问题具有最优子结构意味着问题的最优解可以由子问题的最优解组合而成。例如,在求解最短路径问题中,从起点到终点的最短路径可以由从起点到中间点的最短路径和从中间点到终点的最短路径组成。
  • 子问题重叠:子问题重叠是指在求解问题的过程中,会多次重复地求解相同的子问题。动态规划通过保存子问题的解,避免了重复计算这些子问题,从而提高了算法的效率。

解题步骤

  • 确定问题的状态:状态是描述问题在不同阶段的特征。选择合适的状态表示是动态规划的关键。例如,在背包问题中,可以用背包的剩余容量和已选物品的集合来表示状态。
  • 定义状态转移方程:状态转移方程描述了如何从一个状态转移到另一个状态。它通常是根据问题的最优子结构性质推导出来的。例如,在背包问题中,状态转移方程可以表示为:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]),其中dp[i][j]表示前i个物品放入容量为j的背包中的最大价值,w[i]和v[i]分别表示第i个物品的重量和价值。
  • 确定初始状态和边界条件:初始状态是问题的最简单情况,边界条件是状态转移方程在特殊情况下的取值。例如,在背包问题中,初始状态可以是dp[0][j] = 0(没有物品时,背包价值为 0),边界条件可以是dp[i][0] = 0(背包容量为 0 时,背包价值为 0)。
  • 计算最优解:根据状态转移方程和初始状态、边界条件,通过递推或递归的方式计算出问题的最优解。通常可以使用自底向上(递推)或自顶向下(记忆化搜索)的方法进行计算。

应用举例

打家劫舍

链接: https://leetcode.cn/problems/Gu0c2T/

一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响小偷偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组nums,请计算不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:nums = [1,2,3,1] 输出:4 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution:
    def rob(self, nums: List[int]) -> int:
        len_nums = len(nums)

        # 定义边界条件
        if len_nums == 0:
            return 0
        if len_nums == 1:
            return nums[0]
        dp = []
        dp.append(nums[0])
        dp.append(max(nums[1], nums[0]))

        for idx in range(2, len_nums, 1):
            dp[idx % 2] = max(dp[(idx - 1) % 2], dp[(idx - 2) % 2] + nums[idx])  # 只需要用两个值来缓存中间结果
        return dp[(len_nums - 1) % 2]
打家劫舍2

链接:https://leetcode.cn/problems/PzWKhm/

环形房屋,即第一个房屋和最后一个房屋也不能同时被打劫。该问题可以看做是求以下两个子问题的最大值:

  • 房屋0 到 房屋len - 2 (不打劫最后一间房屋)
  • 房屋1 到 房屋len - 1 (不打劫第一间房屋)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def helper(nums, start, end):
    dp = []
    dp.append(nums[start])
    if start < end:
        dp.append(max(nums[start], nums[start + 1]))
    for i in range(start + 2, end + 1, 1):
        j = i - start
        dp[j % 2] = max(dp[(j - 1) % 2], dp[(j - 2) % 2] + nums[i])
    return dp[(end - start) % 2]


class Solution:
    def rob(self, nums: List[int]) -> int:
        len_nums = len(nums)
        if len_nums == 0:
            return 0
        if len_nums == 1:
            return nums[0]
        return max(helper(nums, 0, len(nums) - 2), helper(nums, 1, len(nums) - 1))
粉刷房子

链接:https://leetcode.cn/problems/JEj789/description/

假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。 当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。 例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。 请计算出粉刷完所有房子最少的花费成本。 示例 1: 输入: costs = [[17,2,17],[16,16,5],[14,3,19]] 输出: 10 解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色。 最少花费: 2 + 5 + 3 = 10。

示例 2: 输入: costs = [[7,6,2]] 输出: 2

  • 状态转移方程为:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
dp[0][i] = min(dp[1][i - 1], dp[2][i - 1]) + cost[i][0]
dp[1][i] = min(dp[0][i - 1], dp[2][i - 1]) + cost[i][1]
dp[2][i] = min(dp[1][i - 1], dp[0][i - 1]) + cost[i][2]

result = min(dp[0][last], dp[1][last], dp[2][last])
  • 初始值:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
dp[0][0] = cost[0][0]
dp[1][0] = cost[0][1]
dp[2][0] = cost[0][2]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def minCost(self, costs: List[List[int]]) -> int:
    len_costs = len(costs)
    last = len_costs - 1
    if len_costs == 0:
        return 0
    dp = [[None for _ in range(len_costs)] for _ in range(3)]
    for i in range(3):
        dp[i][0] = costs[0][i]
    for i in range(1, len_costs):
        dp[0][i] = min(dp[1][i - 1], dp[2][i - 1]) + costs[i][0]
        dp[1][i] = min(dp[0][i - 1], dp[2][i - 1]) + costs[i][1]
        dp[2][i] = min(dp[1][i - 1], dp[0][i - 1]) + costs[i][2]
    return min(dp[0][last], dp[1][last], dp[2][last])
翻转字符

链接: https://leetcode.cn/problems/cyJERH/description/

如果一个由 ‘0’ 和 ‘1’ 组成的字符串,是以一些 ‘0’(可能没有 ‘0’)后面跟着一些 ‘1’(也可能没有 ‘1’)的形式组成的,那么该字符串是 单调递增 的。 我们给出一个由字符 ‘0’ 和 ‘1’ 组成的字符串 s,我们可以将任何 ‘0’ 翻转为 ‘1’ 或者将 ‘1’ 翻转为 ‘0’。 返回使 s 单调递增 的最小翻转次数。

示例 1:

输入:s = “00110” 输出:1 解释:我们翻转最后一位得到 00111.

示例 2:

输入:s = “010110” 输出:2 解释:我们翻转得到 011111,或者是 000111。

示例 3:

输入:s = “00011000” 输出:2 解释:我们翻转得到 00000000。

  • 分析状态转移方程:
    • dp_f: 把当前字符翻转成”0”且符合单调递增条件的最小翻转次数
    • dp_g: 把当前字符翻转成”1”且符合单调递增条件的最小翻转次数
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
dp_f[i] = dp_f[i - 1] if s[i] == "0" else dp_f[i - 1] + 1
dp_g[i] = min(dp_f[i - 1], dp_g[i - 1]) if s[i] == "1" else min(dp_f[i - 1], dp_g[i - 1]) + 1

寻找初始值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
dp_f[0] = 0 if s[0] == "0" else 1
dp_g[0] = 0 if s[0] == "1" else 1
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution:
    def minFlipsMonoIncr(self, s: str) -> int:
        len_s = len(s)
        if len_s <= 1:
            return 0
        f_cache = 0 if s[0] == "0" else 1
        g_cache = 0 if s[0] == "1" else 1
        dp_f = f_cache
        dp_g = g_cache
        for i in range(1, len_s):
            dp_f = f_cache if s[i] == "0" else f_cache + 1
            dp_g = min(g_cache, f_cache) if s[i] == "1" else min(g_cache, f_cache) + 1
            f_cache, g_cache = dp_f, dp_g
        return min(dp_f, dp_g)
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-09-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【算法/训练】:动态规划DP
动态规划(Dynamic Programming,DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题
IsLand1314
2024/10/15
4200
【算法/训练】:动态规划DP
【算法专题】动态规划之简单多状态 dp 问题
题目:一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。 在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
YoungMLet
2024/03/01
1840
【动态规划】陶然无喜亦无忧,人生且自由 - 简单多状态模型
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
用户11369350
2024/12/26
820
【动态规划】陶然无喜亦无忧,人生且自由 - 简单多状态模型
JS算法之动态规划
今天,我们继续探索JS算法相关的知识点。我们来谈谈关于「动态规划」的相关知识点和具体的算法。
前端柒八九
2022/12/19
6.2K0
JS算法之动态规划
简单多状态DP问题
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
南桥
2024/09/20
1050
简单多状态DP问题
每周算法:斐波那契数列模型+路径问题+简单多状态dp+子数组系列
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
啊QQQQQ
2024/11/19
900
每周算法:斐波那契数列模型+路径问题+简单多状态dp+子数组系列
【LeetCode】动态规划 刷题训练(五)
cost数组的横坐标 代表 N号房子,纵坐标 代表 颜色 在每号房子中分别选取一种颜色,但是相邻之间不能选取相同的颜色,求最小花费
lovevivi
2023/10/17
2490
【LeetCode】动态规划 刷题训练(五)
算法之动态规划
动态规划(Dynamic Programming,简称DP)算法是一种通过将问题(比较复杂)划分为相互重叠的子问题(简单易于求解),并解决子问题来解决原问题的方法。它通常用于优化问题,其中需要找到最优解或最大/最小值。
九转成圣
2024/04/10
1650
算法之动态规划
动态规划入门看这篇就够了,万字长文!
今天是小浩算法 “365刷题计划” 动态规划 - 整合篇。大家应该期待已久了吧!奥利给!
程序员小浩
2020/05/08
1.6K0
动态规划入门看这篇就够了,万字长文!
算法:动态规划
上面是一个按照时间段发生的任务a,b,c,d,e,f,g,h,有的任务之间会有时间重叠。定义:
用户3578099
2022/04/18
1.7K0
算法:动态规划
漫画:动态规划系列 第六讲
在前两篇中,我们分别学习了 “三角形最小路径和” 以及“矩形最小路径和” 的问题,相信已经掌握了这类题型的解题方式。我们只要明确状态的定义,基本上都可以顺利求解。
程序员小浩
2020/03/31
3780
【动态规划】【简单多状态 dp 问题】按摩师、打家劫舍、删除并获得点数、粉刷房子
这题其实和“打家劫舍”问题很像,取完一个数之后,就不能取相邻的数了,还要取的值最大
椰椰椰耶
2024/11/15
900
【动态规划】【简单多状态 dp 问题】按摩师、打家劫舍、删除并获得点数、粉刷房子
用javascript分类刷leetcode3.动态规划(图文视频讲解)
动态规划,英文:Dynamic Programming,简称DP,将问题分解为互相重叠的子问题,通过反复求解子问题来解决原问题就是动态规划,如果某一问题有很多重叠子问题,使用动态规划来解是比较有效的。
js2030code
2022/11/18
8820
动态规划一篇就够了 全网第二详细, 逐步理解, 万字总结
动态规划,一直以来听着就是一种很高深莫测的算法思想。尤其是上学时候算法的第一堂课,老师巴拉巴拉列了一大堆的算法核心思想,贪心、回溯、动态规划... ...,开始感觉要在算法世界里游刃有余的进行解决各种各样牛B问题了,没想到的还是稀里糊涂学过了之后还就真的是学过了(大学的课程还真是一个样子)。再后来才明白,大学的课程一般来说就是入门级讲解,用来开拓眼界的,真正想要有一番自己的见解,必须要在背后下一番辛苦,形成自己的思考逻辑。
Python编程爱好者
2020/09/03
6.5K2
动态规划一篇就够了 全网第二详细, 逐步理解, 万字总结
数据结构与算法 | 动态规划算法(Dynamic Programming)
上一篇文末已经提到了记忆化搜索是动态规划(Dynamic Programming)的一种形式,是一种自顶向下(Top-Down)的思考方式,通常采用递归的编码形式;既然动态规划有自顶向下(Top-Down)的递归形式,自然想到对应的另外一种思考方式自底向上( Bottom-Up ),也就是本篇要写的内容。
Java研究者
2023/11/16
5380
数据结构与算法 | 动态规划算法(Dynamic Programming)
动态规划 —— dp 问题-粉刷房子
https://leetcode.cn/problems/JEj789/description/
迷迭所归处
2024/11/19
630
动态规划 —— dp 问题-粉刷房子
动态规划LeetCode[简单]题全解
在文章[LeetCode]动态规划及LeetCode题解分析中,Jungle介绍到求解动态规划类问题,一般分为三个步骤,这里做个简单回顾:
用户6557940
2022/07/24
2540
[LeetCode]动态规划之打家劫舍ⅠⅡⅢ
在文章[LeetCode]动态规划及LeetCode题解分析中,Jungle介绍到求解动态规划类问题,一般分为三个步骤:
用户6557940
2022/07/24
3000
[LeetCode]动态规划之打家劫舍ⅠⅡⅢ
【算法日记】从零开始认识动态规划(一)
动态规划(Dynamic Programming),简称DP。动态规划的核心是依次解决子问题,通过状态转化得到最终的结果。也就是说,针对可以划分成若干子问题的问题,我们可以使用动态规划来进行解决。
叫我龙翔
2025/01/10
1381
【算法日记】从零开始认识动态规划(一)
算法训练之动态规划(五)——简单多状态问题
可以看到题目要求给房子上颜色,并且相邻的房子颜色不能相同~这显然是是一个多状态的问题,接下来我们来一步步分析一下~
用户11352420
2025/04/12
510
算法训练之动态规划(五)——简单多状态问题
推荐阅读
相关推荐
【算法/训练】:动态规划DP
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验