动态规划(DP算法)详解

内容太多了不作介绍,重点部分是无后效性,重叠子问题,最优子结构。

无后效性:未来的状态不会影响过去的状态,如果我在P1->P2的时候,S->P1多了一条路出来,那么先前保留的路径数就是错误的。

经典的数塔问题也是dp算法的入门问题之一

假设你有这么一个数塔,你的目标是求最底层到最高层,求出最大路径和

比如3->7->2->9这个路径,他的路径和是3+7+2+9

不难发现如果要求到9的最大路径和,首先要求出他前一层的最大路径

核心代码dp[i][j]=max(dp[i-1][j],dp[i-1][j+1])+a[i][j]

dp[i][j](9的最大路径和)

a[i][j](9自己)

dp[i-1][j](前一层5的最大路径和)dp[i-1][j+1](前一层2的最大路径和)

在前一层的最大路径和取大的那一个

例题:[NOIP2005普及组]采药-洛谷

这道题输入这么少也不用scanf了,直接上cin加上小优化

inlinevoidscan(){ios::sync_with_stdio(false);//解除与scanf和cout的同步,具体体现在缓冲区cin.tie(nullptr),cout.tie(nullptr);//可以加快一点速度cin>>T>>m;for(inti=1;i<=m;++i)cin>>t[i]>>v[i];}很明显就是各个最优子问题的问题,要取到最多的价值,必然前一个状态也是最多的。

作出以下定义

显而易见不能从采最后几朵开始往前判断,前面状态都不清楚,怎么可以从后面开始。

AC代码

#includeusingnamespacestd;intT,m,t[101],v[101],dp[101][1001];intmain(){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin>>T>>m;for(inti=1;i<=m;++i)cin>>t[i]>>v[i];for(inti=1;i<=m;++i){for(intj=1;j<=T;++j){if(j>=t[i])dp[i][j]=max(dp[i-1][j],dp[i-1][j-t[i]]+v[i]);elsedp[i][j]=dp[i-1][j];}}cout<

dp代码

for(inti=1;i<=m;++i)for(intj=T;j>=t[i];j--)dp[j]=max(dp[j-t[i]]+v[i],dp[j]);内部循环必须从大到小,因为先前是应用了i-1先前状态去得出i状态,但是此处舍去了一维之后就会导致两者状态都会出现在这个小小的一维数组里面。

比如说用线段来表示所有情况,蓝色的得出了红色的状态。

但是如果我只剩下了一条线段呢?

如果中间被前面先修改了,那么后面要更新状态的时候用的就是红色,而不是我们所需要的蓝色。

所以只能从后往前去推状态才能保证我们所需要的一直是蓝色,而不会被更新成红色。

发现了吧,重点在于找出继承状态(递推式),比如定义的是前n个人,而不是任意n个人,这样n-1和n的区别就在于多了一个人,只要让先前状态抽出能满足多一个人的情况,那就是后者的状态。

例题:5倍经验日-洛谷

定义dp[i][j]为前i个人用j个药可以获得的最大经验值然后就可以得出递推式

for(inti=1;i<=n;++i)for(intj=1;j<=x;++j)if(j>=use[i])dp[i][j]=max(dp[i-1][j]+lose[i],dp[i-1][j-use[i]]+win[i]);elsedp[i][j]=dp[i-1][j]+lose[i];如果能打过,那么我可以选择打或者不打如果打不过,那只能不打但是这道题有一个注意点是,J可以从0开始,因为存在不用药就可以打过的情况,所以先初始化不用药的情况。

初始化

for(inti=1;i<=n;++i)if(use[i]==0)dp[i][0]=dp[i-1][0]+win[i];elsedp[i][0]=dp[i-1][0]+lose[i];AC代码

#includeusingnamespacestd;constintMAXN=1e3+1;longlongn,x,win[MAXN],lose[MAXN],use[MAXN],dp[MAXN][MAXN];//dp[i][j]前i个人,使用j个药,能获得的最大经验值intmain(){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin>>n>>x;for(inti=1;i<=n;++i){cin>>lose[i]>>win[i]>>use[i];}for(inti=1;i<=n;++i)if(use[i]==0)dp[i][0]=dp[i-1][0]+win[i];elsedp[i][0]=dp[i-1][0]+lose[i];for(inti=1;i<=n;++i)for(intj=1;j<=x;++j)if(j>=use[i])dp[i][j]=max(dp[i-1][j]+lose[i],dp[i-1][j-use[i]]+win[i]);elsedp[i][j]=dp[i-1][j]+lose[i];cout<

和上上题采药的区别就是,每个药可以无限采

每条线的含义都是一样的,也就是每个药都只能采一次。

这道题每个药都可以无限采,也就是说同一行之间也要去迭代所有情况

#includeusingnamespacestd;constintMAXM=1e4+1;constintMAXT=1e7+1;intT,m,t[MAXM],v[MAXM];longlongdp[MAXT];intmain(){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin>>T>>m;for(inti=1;i<=m;++i){cin>>t[i]>>v[i];}for(inti=1;i<=m;++i){for(intj=t[i];j<=T;++j){dp[j]=max(dp[j],dp[j-t[i]]+v[i]);}}cout<

如果要写出二维dp首先要改变定义,因为一个是只能用一次,一个是无限用,递推式必然不一样

而是在于如何分析出这道题是dp,比如把2022拆分成不同质数之和,质数最多可以有几个。

如果能想到2022是一个背包,里面放的素数是容量大小,抽象成01背包问题,那问题就会变的非常简单。

这点完全考验的是一个人的思维深度和广度,所以本蒟蒻不行(逃)

THE END
1.动态规划学习:数塔问题详尽分析动态规划数塔问题讲解数塔问题是我们学习动态规划的入门问题: 数字三角形(POJ1163) **在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。 路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为0-99。 https://blog.csdn.net/hjf1201/article/details/78598744
2.动态规划—数塔问题如上图(图片来自网络)是一个数塔,从顶部出发在每一个节点都只能走到相邻的节点,也就是只能向左或者向右走,一直走到底层,要求找出一条路径,使得路径上的数字之和最大。 首先需要将https://www.jianshu.com/p/2a7f5cac0d58
3.Python动态规划经典数塔问题动态规划算法数塔问题动态规划将一个复杂的问题分解为若干个子问题,通过综合子问题的最优解来得到原问题的最优解。需要注意的是动态规划会将每个求解过的子问题的解记录下来,下次遇到同样的子问题可以直接使用记录的结果。用这种方法提高计算效率。 思考一 从数塔顶部向下走,每次都有两种路径选择:向左下走或右下走,最简单的方法可以枚举https://blog.51cto.com/u_15444/9226309
4.数塔问题,简单的动态规划算法编程菜鸟这道题如果用枚举法,在数塔层数稍大的情况下(如40),则需要列举出的路径条数将是一个非常庞大的数目。 如果用贪心法又往往得不到最优解。 在用动态规划考虑数塔问题时可以自顶向下的分析,自底向上的计算。 从顶点出发时到底向左走还是向右走应取决于是从左走能取到最大值还是从右走能取到最大值, https://blog.sina.com.cn/s/blog_a636512c010117z0.html
5.CUSTDP训练第一题数塔问题Input输入数据首先包括一个整数C,表示测试实例的个数,每个测试实例的第一行是一个整数N(1 <= N <= 100),表示数塔的高度,接下来用N行数字表示数塔,其中第i行有个i个整数,且所有的整数均在区间0,990,99内。 Output对于每个测试实例,输出可能得到的最大和,每个实例的输出占一行。 https://blog.nowcoder.net/n/f6cd845b36214d95bc505597cbbbd0a3
6.《信息学奥赛一本通》:第9章第1节动态规划基础(C++版)2018第九章 动态规划 第一节 动态规划的基本模型 第二节 背包问题 第三节 动态题 动态规划程序设计是对解最优化问题的一种途径、一种方 法,而不是一种特殊算法。不像前面所述的那些搜索或数值计 算那样,具有一个标准的数学表达式和明确清晰的解题方法。 动态规划程序设计往往是针对一种最优化问题,由于各种 问题的https://max.book118.com/html/2020/0713/6150021122002220.shtm
7.动态规划DP算法详解最后,也是状态最重要的特点,状态间的转移完全依赖于各个状态本身,如最长递增子序列中,dp[x]的值由 dp[i](i < x)的值确定。若我们在分析动态规划问题的时候能够找到这样一个符合以上所有条件的状态,那么多半这个问题是可以被正确解出的。所以说,解动态规划问题的关键,就是寻找一个好的状态。https://removeif.github.io/algorithm/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92DP%E7%AE%97%E6%B3%95%E8%AF%A6%E8%A7%A3.html
8.ReactCSP问题严格动态腾讯云开发者社区是指在React框架中,由于浏览器的安全策略限制,无法动态加载外部脚本或样式表文件。这种限制可能会导致一些开发需求无法满足,例如在组件渲染过程中根据条件加载不同的脚本或样式表。 为了解决React-CSP问题严格动态,可以采取以下方法: 静态加载:在React组件的渲染过程中,将所有可能需要的脚本和样式表文件都静态地引入到HTMLhttps://cloud.tencent.com/developer/information/React-CSP%E9%97%AE%E9%A2%98%E4%B8%A5%E6%A0%BC%E5%8A%A8%E6%80%81
9.问题列表1211 数组元素的插入 数组问题 入门 8850 1212 移动数组元素 数组问题 入门 5392 1213 删除数组的最小数 数组问题 入门 6038 1214 在最大数后面插入一个数 数组问题 入门 4766 1215 Fish学数学 数组问题 入门 2105 1216 数塔问题 递推动态规划 基础 8602 https://oj.czos.cn/problem/index?cid=1313&pid=14&page=3
10.递归经典问题(2)台阶问题---动态规划算法 问题描述所谓的台阶问题就是说,从0开始上台阶1,2,3n,每次只能上1个或者2个台阶。问上到n个台阶有多少种走法。这个问题是比较典型的,也有很多种变形,我们先讲解下这种的实现。问题分析 我们先按照举例来分析,我测试了下,6个台阶时候的变化,如下个表台阶数走法数1122334558 6 13https://www.pianshen.com/article/1302294396/
11.[NOIP2001]最大公约数和最小公倍数问题输入二个正整数x0,y0(2<=x0<100000,2<=y0<=1000000),求出满足下列条件的P,Q的个数 条件: 1.P,Q是正整数 2.要求P,Q以x0为最大公约数,以y0为最小公倍数. 试求:满足条件的所有可能的两个正整数的个数. 【输入格式】 输入文件为gcdpro.in。 一行,二个正整数x0,y0。 【输出格式】 http://razxhoi.21cnjy.net/mod/programming/view.php?id=4253
12.CSUOJProblem IDTitleSource/CategorySolvedSubmit APIPI打怪简单 贪心1601261 B木匠PIPIⅡ简单 贪心147697 C22-数组-2-爬数塔简单 动态规划149439 D---简单 动态规划141474 E香甜的黄油图论-最短路183542 FK好数动态规划120351 G摆动序列DFS127222 H最小区间覆盖问题简单 贪心124397 Ihttp://vlab.csu.edu.cn/oj/contest.php?cid=1105
13.c++语言经典数塔路径三、C++语言实现数塔问题的动态规划算法 四、总结 正文 一、数塔问题概述 数塔问题是动态规划领域的经典问题之一。问题描述如下:给定一个数塔,每层都有若干个节点,节点的值分别为塔顶到当前节点的整数。要求从塔顶走到塔底,每次只能向下移动一个节点,求所有路径中节点值之和的最大值。 二、动态规划解决数塔问题https://wenku.baidu.com/view/26cfb96eadaad1f34693daef5ef7ba0d4a736dcc.html