当前位置: 首页 > news >正文

【算法】——动态规划算法及实践应用

目录

前言:

一、什么是动态规划算法?

1. 简单介绍

2. 如何实现

二、实践案例

1. 第n个泰波那契数

2. 三步问题

3. 解码方法

总结:


前言:

本篇文章会介绍什么是动态规划算法,并会给出一些实践例题,方便我们快速掌握。

一、什么是动态规划算法?

1. 简单介绍

动态规划的核心思想是:将复杂的大问题拆解成一系列小问题,通过解决并“记住”这些小问题的答案,来避免重复计算,从而高效地解决大问题。

这过程就用到了迭代的思想,即由小推大,一步步接近最终目标。

2. 如何实现

动态规划算法的实现一般有以下几个步骤:

  1. 定义状态表示
  2. 找出状态转移方程(关键步骤)
  3. 初始化
  4. 确定计算顺序
  5. 返回结果

接下我会对这几个步骤进行讲解:

首先动态规划一般会有一个dp表,用于存储历史数据,我们先假设这个dp表是一个一维数组。那么第一步定义状态表示就是定义dp[ i ]到底表示了什么;第二步就是根据已有数据如果推出下一个值dp[ i + 1 ],比如dp[ i +1 ] = dp[ i ] + dp[ i - 1 ];第三步初始化就是确定边界情况,即dp表中前几个数据的值;第四步确定计算顺序就是根据题目具体情况选择从哪边开始计算,再循环遍历;第五步返回dp表中最终的计算结果即可。

二、实践案例

光看概念还是比较抽象,所以我们接下来代入一些例子来进行讲解,让你快速上手动态规划。

1. 第n个泰波那契数

首先从一个简单的题目开始,走一走动态规划的五个步骤:

  • 第一步,定义状态表示,先定义一个dp表,dp[ i ]就表示下标为i的泰波那契数的值
  • 第二步,状态转移方程,本题题目中已经给出,dp[n+3] = dp[n] + dp[n+1] + dp[n+2]
  • 第三步,初始化,由于状态转移方程需要至少三个值,所以前三个泰波那契数都需要进行初始化,分别初始化为0,1,1
  • 第四步,本题的计算顺序是从小推大
  • 第五步,最终返回dp[n]的值即可

示例代码:

    int tribonacci(int n) {// 处理边界情况if(n == 0) return 0;if(n == 1 || n == 2) return 1;vector<int> dp(n+1);dp[0] = 0; dp[1] = 1; dp[2] = 1;   // 初始化dp表for(int i = 3; i <= n; i++){    dp[i] = dp[i-1] + dp[i-2] + dp[i-3];// 根据状态转移方程推导dp表}return dp[n];}

2. 三步问题

不同于泰波那契数列,本题就需要我们自己去推一推各个步骤了。

首先我们用dp[n]来表示上到n阶台阶有的方式个数(为了方便数据的映射,我们选择浪费掉dp[0]这个空间),dp[1]只有一种方法,所以dp[1] = 1;dp[2]可以直接从0阶到2阶,也可以从1阶到2阶,所以dp[2] = dp[1] + 1 = 2;dp[3]可以直接从0到3阶,也可以先到1阶再一步跨到3阶,也可以先到2阶再一步跨到3阶,所以dp[3] = dp[1] + dp[2] + 1 = 4。

由此我们可以推断出更广泛的情况,即到第n阶台阶可以有先到n-1或n-2或n-3的台阶后,再跨一步到达n阶台阶,所以状态转移方程为dp[n] = dp[n-1] + dp[n-2] + dp[n-3]。

推导出核心的状态转移方程后遍历,最后输出结果即可。

示例代码:

    int waysToStep(int n) {const int mod = 1e9 + 7; // 模数if(n == 1 || n == 2)return n;if(n == 3)return 4;vector<int> dp(n+1);dp[1] = 1; dp[2] = 2; dp[3] = 4;for(int i = 4; i <= n; i++){dp[i] = ((dp[i-1] + dp[i-2])%mod + dp[i-3])%mod;}return dp[n];

由于本题数据较大,需要进行取模运算

3. 解码方法

这道题比前两道都要复杂一点,我们先初步定义dp[i]表示解码到下标为i位置的字符串时解法的个数,那么推导dp[i]的状态转移方程就要分为两种情况,第一种:s[i]作为一位数字解码,如果满足  '1' <= s[i] <= '9',那么就可以编码,dp[i] += dp[i-1]。第二种:s[i-1]和s[i]组合成两位数进行解码,如果两位数组合起来>=10且<=26,那也可以编码,dp[i] += dp[i-2]。

dp[0]取决于第一个字符是否为'0',而dp[1]就相对复杂,和后续的dp[i]状态转移方程的情况类似,但又因为数组范围的问题导致要单独处理。

所以这里在处理初始化时用到了一点小技巧,避免了dp[1]的单独处理,即主动浪费第一个空间,这样dp在用状态转移方程时就不会超出数组范围。但是相应的,s[ ]和dp[ ]的映射关系也要改变,dp[ ]的所有数值都要后移一位,多出来的dp[0]的数值则要根据题目来设置,如本题dp[0]就等于1。

最终示例代码:

    int numDecodings(string s) {int n = s.size();vector<int> dp(n+1, 0);dp[0] = 1;dp[1] = s[0] != '0';if(n == 1) return dp[1];for(int i = 2; i <= n; i++){// dp[i]单独解码if(s[i-1] != '0'){dp[i] += dp[i-1];}// dp[i]和dp[i-1]一起解码int val = (s[i-2] - '0')*10 + s[i-1]-'0';if(val >= 10 && val <= 26){dp[i] += dp[i-2];}}return dp[n];}

总结:

这里举了三道例题来讲解动态规划算法,但学会了这三道题也只是对动态规划算法有了初步认识,后续还需要练习更多的动态规划的算法题来进一步掌握。

以上便是本篇文章的所有内容了,如果觉得又帮助的话可以点赞收藏加关注支持一下!

http://www.dtcms.com/a/441722.html

相关文章:

  • 鲜花网站建设项目策划书contrast wordpress
  • 洛谷 - dp 题目详解(超详细版)
  • 课题学习(二十四)---专栏终章:基于四元数和扩展卡尔曼滤波的姿态解算算法(MPU9250+STM32F103ZET6)
  • [GESP202403 五级] B-smooth 数
  • Ext2文件系统
  • 【剑斩OFFER】算法的暴力美学——无重复字符的最长字串
  • LeetCode 437. 路径总和 III
  • LeetCode-hot100——​二叉搜索树中第k小的元素​
  • 算法基础 典型题 单调栈
  • 人工智能赋能传统医疗设施设备改造:路径、挑战与未来展望
  • 【Java】杨辉三角、洗牌算法
  • 密码学中的Salt
  • 嵌入式硬件——基于IMX6ULL的GPT(通用定时器)实现
  • 东莞 营销网站建设互动网站如何做
  • 【pytest】使用 marker 向 fixture 传递数据
  • 从0死磕全栈之Next.js 中间件(Middleware)详解与实战
  • 用个人电脑做服务器建网站天门市基础建设网站
  • 分布式专题——26 BIO、NIO编程与直接内存、零拷贝深入辨析
  • Redisson分布式限流
  • 计算机网络-应用层协议原理
  • 分布式文件存储系统FastDFS(入门)
  • 电机控制-PMSM无感FOC控制(五)相电流检测及重构 — 单电阻采样
  • C语言底层学习(4.数据在内存中的存储)
  • 虚幻引擎UE5专用服务器游戏开发-33 在上半身播放组合蒙太奇
  • 织梦网站栏目访问目录做网站建设哪家效益快
  • 『数据结构』消失的数字
  • 鹤山网站建设易搜互联湖南seo
  • ORB_SLAM2原理及代码解析:Tracking::CreateInitialMapMonocular() 函数
  • 【Linux】System V —— 基于建造者模式的信号量
  • VScode-ESP-IDF工程函数定义无法跳转且无注释提示