老壁灯带你入门动态规划

1. 什么是动态规划

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。

从字面意义上来理解,就是走一步看一步,边解决问题,边对问题进行整体规划

其实,动态规划的本质就是将问题拆分为小的子问题,对小问题的一步步解决,小问题渐渐积累为较大的问题,最终就可以解决掉整个问题。

这似乎与递归解决问题的思路很像,但是动态规划一般采用的是迭代的方法。

在解决问题的过程中,我们一般会记录下小问题的解决能带给我们的有效信息,从而利用小问题解决大问题,这样就可以避免对某些问题的重复计算。

动态规划不是什么固定的解题套路,而是提供了一种解题的思路。

动态规划的核心

动态规划的核心在于对两个概念的确定:

1. 当前状态的表示(小问题)

2. 状态之间的递推关系(小问题与较大问题之间的联系)

什么问题可以用动态规划解决

如果一个问题,可以把所有可能的答案穷举出来,并且穷举出来后,发现存在重叠子问题,就可以考虑使用动态规划。

例如,求最大值/最小值,可不可行,是不是,方案个数等

 2. 初次遇见动态规划

光是看上面的介绍,似乎有点晕头转向不知所云。

那么,不妨更着博主的学习路径,来看看动态规划到底是怎么回事。


兑换零钱

这是leetcode上的一道题,刷题链接:. - 力扣(LeetCode)

我便是在遇到这道题时,初次见识了动态规划。 

  

 1. 回溯解法

询问可能性,这让我想到了之前做过的N皇后的问题:C语言解决N皇后问题-CSDN博客

于是我们首先考虑试试回溯的算法。

思路

1. 每次调用函数选择一个硬币,将amount减去该硬币的值之后,传给下一次调用。

2. 依次传递下去,如果某次函数接收到的amount为0,则说明找到了一种可行的组合,此时返回1。

3. 如果某次函数接收到的amount为1,则说明该种组合不可行,此时返回0。

4. 为了避免重复,我们每次传给下一次调用的coins数组都不包括在该硬币位置之前的硬币。

例如,在示例1所给数据下,如果每次传入全部的coins,则会出现诸如2 1 1 1和1 2 1 1的重复情况。 

 代码
int change1(int amount, int* coins, int coinsSize) 
{if(amount == 0)return 1;if(amount < 0||coinsSize <= 0)return 0;int sum = 0;for(int i = 0; i < coinsSize; i++){sum += change1(amount - coins[i], coins + i, coinsSize - i);}return sum;
}
总结

这种解法,虽然极力避免了对重复情况的考虑,但是其递归的本质,还是使其在处理较大数据时会超时。

而且,相比于动态规划,这种解法似乎有点无头苍蝇的感觉,对所有情况进行无差别尝试。

回溯的解法,其运算过程像是树一样,不断展开,每一种情况对应树的一个末梢。

有了这个树状的结构,我们其实可以将每种情况是什么一并打印出来,但是这道题只要求我们求出可行方案的种数。

所以,我们似乎还是做了很多不必要的工作,我们的小问题留下的信息太多了,我们每次计算做的事也太多了。

仔细回顾这道题,我们会发现,如果将我们的思路总结成递归的基本思路,就会是如下这样:

1. 要知道coins组合成amount有多少种方法(W_{aomunt}),我们首先要知道组合成amount-coins[i]有多少种方法(W_{amount-coin[i]})。

2. 那么W_{amount} = \sum_{i=1}^{n} W_{amount-coins[i]}

但是,我们在解决的过程中,不仅把 W_{amount-coin[i]}记录了下来,还将得到其的路径也记录了下来。

而且,在不同的深度中amount-coins[i]的大小可能相同,这也就导致我们还是不可避免地做了很多重复的工作。

于是我在官方的解答中,首次见识了动态规划是如何解决问题的。

2. 动态规划解法

思路

1. 与前面回溯解法总结后的思路相同,我们也要先求得W_{amount-coin[i]},但是我们只记录在解决这个问题得到的信息中,我们需要的,也就是其值。

2. 状态的表示:我们用一个一维数组来记录每种金额的组合数,元素下标就代表金额。这样,对于每种可能出现的金额(amount-coins[i]),我们就仅会做一次计算。

3. 状态之间的关系:也就是W_{amount} = \sum_{i=1}^{n} W_{amount-coins[i]}。只要知道较小金额的组合种数,就可以依次递推得到较大金额的组合种数。

4. 结果:我们要求的,其实也就是下标为amount的元素的大小。

5. 下标为0的元素值赋为1,因为得到总金额为0的组合有且仅有一枚硬币都没有的组合。

6. 数组默认初始化为0,这样一来,如果不存在可以组成amount-coins[i]的组合,那么W_{amount-coin[i]}W_{aomunt}的贡献就为0。

代码
int change2(int amount, int* coins, int coinsSize)
{int dp[amount + 1];memset(dp, 0, sizeof(dp));dp[0] = 1;for(int i = 0; i < coinsSize; i++){for(int j = coins[i]; j <= amount; j++){dp[j] += dp[j - coins[i]];}}return dp[amount];
}
总结

动态规划的算法通常采用迭代来实现,这不仅解决了递归,回溯的缺陷,而且真正意义上做到了杜绝重复子问题的运算。

并且,在解决问题的过程中,充分利用了子问题留下的有效信息来解决当前问题。

递归时,只能利用自己所在分支的信息,而动态规划可以对全局的信息进行利用。

但是,动态规划的不足就在于其实在不好想到,且没有固定的套路,是考验个人能力的好手段。

3. 尝试自己解决动态规划问题

这是牛客网上的一道题,刷题链接:公共子串计算_牛客题霸_牛客网

 思路

1. 这道题是要我们找两个字符串的公共子串,这使得我会想起之前研究过的kmp算法(从一无所有的角度出发,带你一步步实现kmp算法-CSDN博客)(对这道题不是很重要,不了解也可),这么一想,似乎得到next数组的思想就像是动态规划。

2. 我们依然想用一个数组来存储有效信息,这个数组的下标表示当前的状态(我正在解决哪个问题),其对应的值就是我们要留下的有效信息。

3. 我们用i和j分别指向两个字符串,当所指两个字符匹配成功时,当前公共子串的长度就加1。所以,要想知道当前公共子串的长度,我们就要知道在这两个字符匹配成功之前公共子串的长度。

4. 那么,这道题用一维数组似乎无法很好地表示当前的状态,因为我们要知道匹配之前公共子串的长度,那么其对应的i和j就都需要能表示出来,我们才能找到它。

5. 状态的表示:于是我们采用二维数组(dp[][])来定义当前状态,元素的下标分别为i和j,表示当前公共子串的末尾在两个字符串中分别在第i个和第j个位置上。

6. 状态之间的关系:如果当前两个字符配对成功则dp[i][j] = dp[i-1][j-1] + 1;如果匹配失败,则dp[i][j] = 0。

7. 结果:我们定义一个变量maxLen来记录目前最长的公共子串长度,如果某位置上dp[i][j] > maxLen,则maxLen = dp[i][j]。将每个位置的公共子串都检查完之后maxLen即是要求结果。

代码

#include <stdio.h>
#include <string.h>int main() 
{char arr1[151] = {0};char arr2[151] = {0};scanf("%s", arr1);scanf("%s", arr2);int len1 = strlen(arr1);int len2 = strlen(arr2);int dp[len1+1][len2+1];int maxLen = 0;memset(dp, 0, sizeof(dp));for(int i = 1; i <= len1; i++){for(int j = 1; j <= len2; j++){dp[i][j] = arr1[i-1] == arr2[j-1] ? dp[i-1][j-1] + 1 : 0;maxLen = maxLen > dp[i][j] ? maxLen : dp[i][j];}}printf("%d\n", maxLen);return 0;
}

总结

动态规划是很值得学习的东西,也是需要费力攻克的难关,绝不是看一两篇文章就能完全掌握的。

本文章只是带领读者初步了解动态规划这种解题思想,能够入门,方便进行更加深入的学习。

博主也是刚刚接触到这类题型,顿时感到未来的路还很长,希望在了解更多之后还有机会分享动态规划学习的文章。

接下来,可以通过背包问题,进行更加深入地学习。

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

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

相关文章

Mac上的Gatekeeper系统跟运行时保护

文章目录 问题&#xff1a;无法打开“xxx.xxx”&#xff0c;因为无法验证开发者。macOS无法验证此App是否包含恶意软件。如何解决&#xff1f; 参考资料门禁运行时保护 问题&#xff1a;无法打开“xxx.xxx”&#xff0c;因为无法验证开发者。macOS无法验证此App是否包含恶意软件…

Leetcode - 2580. 统计将重叠区间合并成组的方案数

文章目录 思路AC CODE总结 题目链接&#xff1a;2580. 统计将重叠区间合并成组的方案数 思路 一个区间合并的板子&#xff0c;计算出区间数目之后&#xff0c;每个区间都有放左和放右两种选法&#xff0c;所以最后的答案就是 2 k 2^k 2k。但是需要用c进行二维数组的排序&…

【正点原子FreeRTOS学习笔记】————(4)FreeRTOS中断管理

这里写目录标题 一、什么是中断&#xff1f;&#xff08;了解&#xff09;二、中断优先级分组设置&#xff08;熟悉&#xff09;三、中断相关寄存器&#xff08;熟悉&#xff09;四、FreeRTOS中断管理实验&#xff08;掌握&#xff09; 一、什么是中断&#xff1f;&#xff08;…

深入理解C语言宏定义

目录 一、前言 二、宏的相关语法 2.1 #define 2.2 #undef 2.3 #运算符 2.4 ##运算符 三、宏替换的规则 四、宏与函数 一、前言 我们都知道#define语句可以定义常量&#xff0c;在编译器预处理时会全部将名字替换为常量。与此同时&#xff0c;#define也允许把参数替换到…

开放大学2024年春《数控技术 060253》综合大作业参考答案

答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 单选题 1数控系统的核心是&#xff08; &#xff09; …

【项目管理——时间管理】【自用笔记】

1 项目时间管理&#xff08;进度管理&#xff09;概述 过程&#xff1a;&#xff08;2—6&#xff09;为规划过程组&#xff0c;7为监控过程组 题目定义&#xff1a;项目时间管理又称为进度管理&#xff0c;是指确保项目按时完成所需的过程。目标&#xff1a;时间管理的主要目标…

Rust GUI学习 小部件系列(一):如何在iced窗口中使用颜色选择器colorpicker

注&#xff1a;此文适合于对rust有一些了解的朋友 iced是一个跨平台的GUI库&#xff0c;用于为rust语言程序构建UI界面。 前言&#xff1a; 本系列是iced的小部件应用介绍系列&#xff0c;主要介绍iced、iced_aw两个库中涉及的各种小部件的使用及实例演示。 本文所介绍的是co…

安捷伦Agilent E5071B网络分析仪

181/2461/8938产品概述&#xff1a; Agilent E5071B 网络分析仪可为射频组件提供快速、准确的测量。与同类网络分析仪相比&#xff0c;其宽动态范围和低迹线噪声可实现更高的测试质量和吞吐量。内置 2、3 和 4 个测试端口可同时测量具有最多四个端口的组件的所有信号路径。Agi…

中国土壤厚度空间分布数据

土壤层次分为覆盖层 林溶层 淀积层 母质层&#xff0c;其中在林溶层中的最上面那层就是我们通常说的土壤厚度在这一层中&#xff0c;这一层也被称为腐殖层&#xff0c;是肥力性质最好的一层&#xff0c;植物根系和微生物也集中在这一层。至于覆盖层在森林土壤中比较常见&#x…

2024年【G3锅炉水处理】考试题及G3锅炉水处理考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 G3锅炉水处理考试题参考答案及G3锅炉水处理考试试题解析是安全生产模拟考试一点通题库老师及G3锅炉水处理操作证已考过的学员汇总&#xff0c;相对有效帮助G3锅炉水处理考试报名学员顺利通过考试。 1、【多选题】锅筒…

vivado 在远程主机上启动作业、ISE命令图、实施类别,战略描述和指令映射

在远程主机上启动作业 一旦配置了远程主机&#xff0c;使用它们启动Vivado作业就很容易了。下图显示了启动运行对话框。启动跑步时&#xff0c;选择“在远程上启动跑步”hosts或Launch在群集上运行&#xff0c;然后选择特定的群集。这些作业将使用您的要执行的预配置设置。 作业…

Leetcode70. 爬楼梯(动态规划)

Leetcode原题 Leetcode70. 爬楼梯 标签 记忆化搜索 | 数学 | 动态规划 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f;示例 1&#xff1a;输入&#xff1a;n 2 输出&#xff1a;2 解…

阿里云服务器安装MySQL(宝塔面板)

只写关键步骤 1. 创建一个云服务器实例 2 修改密码&#xff0c;登录服务器 3. 安装宝塔面板 进入https://www.bt.cn/new/index.html 进入宝塔面板地址 4. 安装Mysql 5. 创建数据库&#xff08;可导入数据库&#xff09; 6. 测试连接数据库 打开Navicat&#xff08;或其他数据…

centos安装jdk的坑

文章目录 一、安装jdk二、查找jdk的目录三、配置JAVA_HOME 一、安装jdk 我们一般用yum search java | grep jdk查询可以安装的jdk 但是一定要注意如下图&#xff0c;必须知道jdk和jre的区别 yum install java-1.8.0-openjdk-devel.x86_64二、查找jdk的目录 用如下命令 sudo…

chrome 浏览器报错 This page will not function without javascript enabled

This page will not function without javascript enabled. Please enable javascript on your browser. 在访问公司spark history 页面时&#xff0c;发现页面加载不全&#xff0c;并提示如上报错&#xff0c;因此按照如下步骤&#xff0c;已解决问题。 在浏览器中启用 JavaS…

OpenLayers6实战,OpenLayers绘制五角星,OpenLayers绘制特殊图形,地图上画五角星

专栏目录: OpenLayers实战进阶专栏目录 前言 本章讲解如何使用OpenLayers6在地图上绘制五角星这种特殊图形的功能。 本章上一章基础上修改而成:OpenLayers6实战,OpenLayers绘制特殊图形,OpenLayers绘制四角形(菱形),OpenLayers绘制菱形 二、依赖和使用 "ol&q…

【APP_TYC】数据采集案例天眼APP查_抓包分析_②

追寻啊 你身影千年 恳请宿命怜 无尽日夜 只为见你一面 我愿化作 窗外的桑花 朝朝暮暮 都有我牵挂 无论冬雪秋沙 海角天涯 缱绻不尽 念你啊 倘若化作 林间的桑花 借一暖风 赠你梦如画 纵使悲忧如雪 一笑融化 &#x1f3b5; Joysaaaa《朝朝如念》 抓包分析 …

.NET开源免费、功能强大的 Windows 截图录屏神器

前言 今天大姚给大家分享一款.NET开源免费&#xff08;基于GPL3.0开源协议&#xff09;、功能强大、简洁灵活的 Windows 截图、录屏、Gif动图制作神器&#xff1a;ShareX。 功能特性 ShareX 是一个开源的屏幕捕捉工具&#xff0c;具有丰富的功能特性&#xff0c;包括但不限于…

洗地机好用吗?哪款型号值得推荐?看完本文你就知道

在如今社会生活节奏不断加快的情况下&#xff0c;洗地机已经成为众多家庭的必备的清洁设备&#xff0c;面对市面上种类繁多的洗地机&#xff0c;我们常常会发出感叹“洗地机好用吗&#xff1f;洗地机哪个型好用&#xff1f;”等的疑问&#xff0c;今天&#xff0c;为了帮助大家…