内容太多了不作介绍,重点部分是无后效性,重叠子问题,最优子结构。
无后效性:未来的状态不会影响过去的状态,如果我在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代码
#include 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代码 #include 和上上题采药的区别就是,每个药可以无限采 每条线的含义都是一样的,也就是每个药都只能采一次。 这道题每个药都可以无限采,也就是说同一行之间也要去迭代所有情况 #include 如果要写出二维dp首先要改变定义,因为一个是只能用一次,一个是无限用,递推式必然不一样 而是在于如何分析出这道题是dp,比如把2022拆分成不同质数之和,质数最多可以有几个。 如果能想到2022是一个背包,里面放的素数是容量大小,抽象成01背包问题,那问题就会变的非常简单。 这点完全考验的是一个人的思维深度和广度,所以本蒟蒻不行(逃)