【数据结构和算法】三、动态规划原理讲解与实战演练

目录

1、什么是动态规划?

2、动态规划实战演练

2.1 力扣题之爬楼梯问题

(1)解题思路1:

(2)解题思路2:

(3)动态规划(DP):解题思路

(4)动态规划理论

2.2 力扣题之海盗船长

(1)解题思路1:

(2)解题思路2:

(3)动态规划:解题思路3:


1、什么是动态规划?

        动态规划(Dynamic Programming,DP)是把复杂问题分解为简单的子问题的求解方法。

        动态规划的基本思想虽然简单,但分解的子问题很多都不一样。所以需要多做一些练习,接触并了解各种子问题及分解的思路,结合着不同数据结构,才能逐渐掌握DP的技巧。

        所以,DP归纳起来就是多做练习,多思考,逐渐摸索题目的思考逻辑和优化思路。才能掌握使用DP解题的技巧。


   从实际问题出发,分几步走,不断练习、迭代熟练:

  1. 暴力求解(枚举)
  2. 拆分子问题(DP)解法
  3. DP原理与题型相结合的分析

2、动态规划实战演练

2.1 力扣题之爬楼梯问题

以力扣著名的题“爬楼梯”为例子:

  • 爬楼梯问题: . - 力扣(LeetCode)

    假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

    每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

    示例 1:

    输入:n = 2
    输出:2
    解释:有两种方法可以爬到楼顶。
    1. 1 阶 + 1 阶
    2. 2 阶
    

    示例 2:

    输入:n = 3
    输出:3
    解释:有三种方法可以爬到楼顶。1. 1 阶 + 1 阶 + 1 阶
    2. 1 阶 + 2 阶
    3. 2 阶 + 1 阶
    

    提示:

    • 1 <= n <= 45

    (1)解题思路1:

    暴力求解,排列所有1,2的组合。

    寻找总和为n的,不同数字长度的组合。

    from itertools import productdef climbStairs(n):pms = []for rep in range(1,n+1):permutations = list(product([1,2],repeat=rep))for pm in permutations:if sum(pm) == n:pms.append(pm)return len(pms)
    

    ※ itertools是python为高效循环而创建迭代器的函数库 。product就是一个笛卡尔积(嵌套for循环)功能实现的函数。

    • 时间复杂度O(n^3)
    • 空间复杂度O(n)

    (2)解题思路2:

    问题寻找的是爬楼梯的方法数量,而不是具体统计每次走几阶。

    所以问题关注的是每次爬1阶、爬2阶两种走法的组合上。

    假设n=3,可以直接排列一下相应的组合

    3 = 1 + 1 + 1
    3 = 2 + 1
    3 = 1 + 2
    

    简单直观,但我们要寻找的爬楼梯的规律。似乎还没有发现,继续增加n的数量~

    当n=4:

    4 = 1 + 1 + 1 + 1
    4 = 2 + 1 + 1
    4 = 1 + 2 + 14 = 1 + 1 + 2
    4 = 2 + 2
    

    当n=5时:

    5 = 1 + 1 + 1 + 1 + 1
    5 = 2 + 1 + 1 + 1
    5 = 1 + 2 + 1 + 15 = 1 + 1 + 2 + 1
    5 = 2 + 2 + 15 = 1 + 1 + 1 + 2
    5 = 2 + 1 + 2
    5 = 1 + 2 + 2
    

            排列组合的方式,让我们似乎观察到了数值的规律。那就是n阶的走法,包含了n-1阶和n-2阶的走法。且 n阶的走法 = (n-1)阶走法 + (n-2)阶走法

    简单的循环遍历就可以完成走法的累加:

    n = 5
    n1,n2 = 1,2  # n=1和n=2情况下的走法
    res = 0
    for i in range(3, n+1):  # 从n=3时开始,直到nres = n1 + n2n1 = n2n2 = res
    print(res)
    

    测试下n=6,结果(n-1) + (n-2) = 8 + 5 = 13

    还可以转换为递归写法

    def climbStairs(n):if n <= 1:return 1return climbStairs(n-1) + climbStairs(n-2) 
    

    至此,就是暴力算法的解题思路,所有可能的组合都做一遍。

    (3)动态规划(DP):解题思路

    依据上面算法特征分析的基础上,使用一个数组动态存储n的计算结果。

    初始化数组,预先存入n-1和n-2的结果

    def climbStairs(n):if n <= 1:return ndp = [i for i in range(n+1)]for i in range(3,n+1):dp[i] = dp[i-1] + dp[i-2]return dp[-1]
    

    上面代码中的dp就是我们提到的数组,其中dp[i] = dp[i-1] + dp[i-2]

    循环执行完成后,数组中最后一次计算的结果就是爬n阶楼梯的方法数。

    • 时间复杂度:O(n)
    • 空间复杂度:O(n)

    对于空间复杂度,还可以进一步优化。由于我们只考虑n以及n-1、n-2的值。所以,中间的过程值可以丢弃。定义一个固定长度的数组,每次计算结果动态覆盖,如下:

    def climbStairs(n):if n <= 1:return ndp = [0,1,2]for i in range(3,n+1):sum = dp[1] + dp[2]dp[1] = dp[2]dp[2] = sumreturn dp[2]
    
    • 时间复杂度:O(n)
    • 空间复杂度:O(1)

    (4)动态规划理论

            学习动态规划算法,是从多种题解的思路中学习和发现。众多技术高手都给出了思路和技巧,但从实际出发,建议还是先埋头做题,积累了练习和分析思考后,再进行看高手的总结和归纳才会有“英雄所见略同”的共识。

    在这之前,针对每道题目的分析,希望能思考如下的几个问题:

    • 题目计算的目标是什么

      走台阶问题的目标是求走法组合总数

    • 计算分解的子问题是什么

      (n-1)阶走法 、(n-2)阶走法就是n阶走法的子问题 ****

    • 转移方程是否是子问题的组合

      转移方程上面已经写出来了 f(n) = f(n-1)+f(n-2) 。

    • 能否优化

      使用动态数组或字典来存储中间结果,可以有效的减少重复计算的复杂度。

      当然,涉及到使用数组等容器来实现计算和优化时,还会接触到背包问题。

2.2 力扣题之海盗船长

问题:海盗船长:. - 力扣(LeetCode)

海盗船长:船长从坐标为(0,0)的位置出发,每次只能向x轴,y轴正方向走一步,求走到x,y点有几种走法?

示例 1:

输入:m = 3, n = 7
输出:28

示例 2:

输入:n = 3, n = 2
输出:3
解释:从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:

输入:m = 7, n = 3
输出:28

示例 4:

输入:m = 3, n = 3
输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 10^9

(1)解题思路1:

从[0,0]到[x,y],是一个从矩阵左上角向右、向下探索的过程。

想要到达[x,y]位置,必然经过[x-1,y]或者[x,y-1]

所以,路径问题的分解就是求[x , y] = [x-1,y] + [x,y-1]

设 m,n = 3,7 暴力解法

results = {}def path_count(x, y):# results = {}if x == 1 or y == 1:results[(x,y)] = 1return 1results[(x,y)] = path_count(x-1,y) + path_count(x,y-1)return results[(x,y)]print(path_count(3,7))

时间复杂度:O(mn)

空间复杂度:O(n)

(2)解题思路2:

观察上面的方案,可以发现分解的路径实际上存在着大量重复的计算

print(results)

我们可以思考:走到results中(m,n)位置的上一步,一定是(m-1,n),(m,n-1),(m-1,n-1)的位置。

这其中(m-1,n-1)走过的路径中,就包含了(m-1,n)和(m,n-1)走过的路径。

以此类推,所有(m-2,n-2)的路径中,也就包含了(m-1,n-1), (m-1,n), (m,n-1) …… 如此嵌套。我们把计算结果存储在results里面,再次遇到相同的计算时,就直接把结果提取出来。

def path_count(x, y):if x == 1 or y == 1:results[(x,y)] = 1return 1# 查找重复节点,直接返回计算后结果if (x,y) in results:return results[(x,y)]results[(x,y)] = path_count(x-1,y) + path_count(x,y-1)return results[(x,y)]

(3)动态规划:解题思路3:

动态规划方式实现:

转移方程:f(x,y) = f(x-1,y) + f(x, y-1)

分两步实现:

  1. 计算“到达”每个位置所需要的步数(初始为1)

    m,n = 3,7
    path_counts = [[1] * n for j in range(m)]
    for i in range(m):for j in range(n):print(path_counts[i][j], end=' ')print()
    
  2. 计算从起始位置,到达当前位置,步数的累加和

    for x in range(1,m):for y in range(1,n):path_counts[x][y] = path_counts[x-1][y] + path_counts[x][y-1]print(path_counts[m-1][n-1])
    

完整代码:

def path_count(x, y):path_counts = [[1] * y for j in range(x)]for i in range(1,x):for j in range(1,y):path_counts[i][j] = path_counts[i-1][j] + path_counts[i][j-1]return path_counts[x-1][y-1]

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

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

相关文章

中天控股智慧园区项目

— 项目概况 — 项目名称&#xff1a;智慧园区项目 项目地点&#xff1a;云南省 合作单位&#xff1a;中天控股集团有限公司&#xff08;简称“中天控股”&#xff09; 汇匠源与中天控股集团有限公司&#xff08;简称“中天控股”&#xff09;曾在智慧园区项目展开合作&a…

前端自学资料(笔记八股)分享—CSS(4)

更多详情&#xff1a;爱米的前端小笔记&#xff08;csdn~xitujuejin~zhiHu~Baidu~小红shu&#xff09;同步更新&#xff0c;等你来看&#xff01;都是利用下班时间整理的&#xff0c;整理不易&#xff0c;大家多多&#x1f44d;&#x1f49b;➕&#x1f914;哦&#xff01;你们…

MySQL查看某个数据库里面每张表的字符集和字符排序集

字符集&#xff1a; 定义了MySQL中数据在硬盘上的存储方式。例如 utfmb3、utfmb4等。每个不同的字符集都拥有一个默认的字符排序集。 字符排序集&#xff1a; 定义了在数据库中进行字符串比较和排序的方式。 &#xff08;1&#xff09;比较字符串&#xff1a;确定两个字符串是否…

Git相关介绍

基本概念 关注&#xff08;watch&#xff09; 关注项目&#xff0c;当项目更新可以接收到通知 事物卡片&#xff08;Issue&#xff09; 发现代码BUG&#xff0c;但是目前没有成型代码&#xff0c;需要讨论时用 Git工作区域 工作区 添加、编辑、修改文件等动作 暂存区 …

坚持使用kimi搭建小程序2小时(04天/05天)

运用好kimi智能助手里面的存储小程序&#xff0c;{缺乏一个相对稳定的反馈体系&#xff0c;自己所挑选的稳定反馈体系就是编程!} 开源竞争&#xff1a; 当你无法彻底掌握一门技术的时候&#xff0c;就开源这门技术&#xff0c;培养出更多的技术依赖&#xff0c;让更多人完善你…

VMware虚拟机启动报错“此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态”

之前正常使用的VMware虚拟机&#xff0c;突然启动时报错&#xff1a;此主机支持 Intel VT-x&#xff0c;但 Intel VT-x 处于禁用状态&#xff0c;详细信息如下截图所示。   百度错误信息&#xff0c;根据参考文献1中的方案&#xff0c;进入BIOS设置启动VT-x。进入BIOS后&…

spyglass关于cdc检测的一处bug

最近在使用22版spyglass的cdc检测功能&#xff0c;发现struct_check的cdc检测实际时存在一些bug的。 构造如下电路&#xff0c;当qualifier和destination信号汇聚时&#xff0c;如果des信号完全将qualifier gate住&#xff0c;sg仍然会报ac_sync。当然此问题可以通过后续funct…

基础知识-因果分析-daythree-独立性检验-贝叶斯公式及应用

根据概率乘法公式有P(AB)P(B|A)P(A)变形为除法形式&#xff0c;则有 更一般地&#xff0c;假设事件的集合B1&#xff0c;B2&#xff0c;…&#xff0c;Bn构成样本空间的一个划分&#xff0c;则根据全概率公式有 将式(2.14)中的B替换为Bi&#xff0c;则有 再代入P(A)的全概率计算…

Openlayers高级交互(8/20):选取feature,平移feature

本示例介绍如何在vue+openlayers中使用Translate,选取feature,平移feature。选择的时候需要按住shift。Translate 功能通常是指在地图上平移某个矢量对象的位置。在 OpenLayers 中,可以通过修改矢量对象的几何位置来实现这一功能。 效果图 配置方式 1)查看基础设置:http…

即插即用篇 | YOLOv8 引入 空间自适应特征调制模块 SAFM

代码地址: https://github.com/sunny2109/SAFMN 论文地址:https://arxiv.org/pdf/2302.13800 虽然已经提出了许多图像超分辨率的解决方案,但它们通常与许多计算和内存限制的低功耗设备不兼容。本文通过提出一个简单而有效的深度网络来高效地解决图像超分辨率问题。具体来说,…

【Oracle实验】字段为空的,无法通过排除判断

Oracle相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 1.场景描述 需求&#xff1a;查询不是某个机构的数据。 同事SQL&#xff1a;where substr(bank_code,1,9) not in(014009001)&#xff1b; 看SQL似乎没什么问题&#xff0c;分析…

练习LabVIEW第二十一题

学习目标&#xff1a; 刚学了LabVIEW&#xff0c;在网上找了些题&#xff0c;练习一下LabVIEW&#xff0c;有不对不好不足的地方欢迎指正&#xff01; 第二十一题&#xff1a; 用一个chart(波形图表)显示一个随机数&#xff0c;用前面板控件改变chart(波形图表)的大小和位置…

CSS.导入方式

1.内部样式 在head的style里面定义如 <style>p1{color: brown;}</style> 2.内联样式 直接在标签的里面定义如 <p2 style"color: blue;">这是用了内联样式&#xff0c;蓝色</p2><br> 3.外部样式表 在css文件夹里面构建一个css文件…

Python | Leetcode Python题解之第515题在每个树行中找最大值

题目&#xff1a; 题解&#xff1a; class Solution:def largestValues(self, root: Optional[TreeNode]) -> List[int]:if root is None:return []ans []q [root]while q:maxVal -inftmp qq []for node in tmp:maxVal max(maxVal, node.val)if node.left:q.append(n…

Android实现扫描二维码条形码,实现扫描快递单,相册图片识别快递单 最慢3秒出回调结果

首先给出我的参考链接地址 Android超方便 集成 Zxing实现扫一扫&#xff0c;闪光灯&#xff0c;生成二维码图片&#xff0c;解析二维码&#xff08;条码&#xff09;等功能 此篇文章发布时间结点是17年。。 当前文章发布时间节点是24年。。 请注意时间节点&#xff0c;以免造成…

vue3中mitt和pinia的区别和主要用途,是否有可重合的部分?

在 Vue 中&#xff0c;Mitt 和 Pinia 是两个不同的工具&#xff0c;它们的主要用途和功能有所不同&#xff0c;但在某些方面也存在重合的部分。 区别 Mitt&#xff1a; Mitt 是一个简单而强大的事件总线库&#xff0c;用于在组件之间进行事件的发布和订阅。 它提供了一种简洁…

雷池社区版compose配置文件解析-mgt

在现代网络安全中&#xff0c;选择合适的 Web 应用防火墙至关重要。雷池&#xff08;SafeLine&#xff09;社区版免费切好用。为网站提供全面的保护&#xff0c;帮助网站抵御各种网络攻击。 compose.yml 文件是 Docker Compose 的核心文件&#xff0c;用于定义和管理多个 Dock…

迭代器边遍历边删除存在的问题

迭代器边遍历边删除存在的问题以及原理 01-问题 ​ 我们先来看看如下代码 public static void main(String[] args) {List<Integer> list new ArrayList<>();list.add(5);list.add(4);list.add(3);list.add(2);list.add(7);list.add(0);Iterator<Integer>…

在 Vue 中如何自动导入项目中的 less 和 scss 变量和文件

在开发时每次使用公共的 less 变量和类名&#xff0c;都要单独导入一下。当文件过多时&#xff0c;会让代码特别的冗余&#xff0c;设置自动导入后会方便很多。 正常使用&#xff1a; <style scoped lang"less"> // 引入 less 文件 import /styles/mixins.le…

【YOLOv11[基础]】实例分割 + 跟踪

Ultralytics YOLO11实例分割涉及识别和概述图像中的单个对象,提供对空间分布的详细了解。与语义分割不同,它唯一地标记并精确地描绘每个对象,这对于物体检测和医学成像等任务至关重要。 在Ultralytics包中有两种类型的实例分割跟踪: 类对象的实例分割:每个类对象被分配一…