目录
一、前置
1.三要素
- 状态
- 转移:状态之间的关系。
- 初始化:状态的边界条件。
2.数字三角形
上图就是一个三行的数字三角形,我们要求的是一个行的数字三角形,每个节点可以走到自己下面的两点从
出发,到第
行,所获的最大节点和(价值)。
走到第行的第
列获得的最大价值,也就是
。这道题首先要找到所有变化的量,把要求得的变化量放在右边(价值),把其余放在左边,这道题是运用了一种最简单的设计状态的方式。首先先不要管维度是否过多,先扔进去,维度少了,解不了题,维度多了可以慢慢优化,删掉无用的维度。
3.数字三角形2
如果把最大节点和变为最大节点和模100(仍为价值),求到第行最大的,显然原来的状态已经不行了,不满足最优子结构,直白来说有反例会与所算出来的结果不同。所以我们可以设计另外一种状态,当存在位置在
价值为
则
,若不存在则为
。也就是将把所有变化量放在左边,把存在性放在右边。
4.斐波那契数列
多种写法,但并不是每一种方法都适用所有DP题,考虑选哪种好做且可用且复杂度尽量小即可。注:纯递归,搜索不是DP。
(1)第一步
int f[114514],n;//定义。
(2)第二步
f[1]=1,f[0]=0;//初始化。
(3)第三步 :四种方式
自己和别人的关系:求别人、求自己。
for(int i=1;i<=n;i++){
f[i+1]+=f[i];
f[i+2]+=f[i];
}//用自己求别人DP。
for(int i=2;i<=n;i++){
f[i]=f[i-1]+f[i-2];
}//用别人求自己DP。
还有一种方法,首先我们可以先看一下递归求斐波那契,首先我们可知一些关系(DP中的转移),也就是,再加上对特判防止终止递归 。
int dfs(int n){
if(n==0)return 0;
if(n==1)return 1;
return dfs(n-1)+dfs(n-2);
}//递归,搜索,O(最后的结果)=O(f[n])。
上方就是递归代码,这个时候我们再加入DP的元素,即可得出一个优化的记忆化搜索。
bool g[114514];//定义,g[i]表示第i个斐波那契数已经求过了。
int dfs(int n){
if(n==0)return 0;
if(n==1)return 1;
if(g[n])return f[n];
g[n]=1;
return f[n]=dfs(n-1)+dfs(n-2);
}//记忆化搜索DP,O(n)避免了重复计算。
5.小小简介准备步入正题
有排列DP、背包DP……而在所有DP中一般DP没有固定格式,是最难的 。
二、排列DP
1.逆序对
- 题目:在
到
的所有排列中逆序对(
且
的一对数)的个数为偶数的有多少个。
- 转移方法:把所有数从大到小或从小到大一个个插入,枚举插入数的位置。
- 状态:
代表1到
已经插入,当前有
个逆序对,方案数为
。
- 转移:发现插入将
插入到第
个位置,会增加
个逆序对且原有
个逆序对,即转移为
。
- 初始化:
。
最后附上代码,还有一个小优化将的代码化为
,最后答案即为
。
f[1][0]=1;
//O(n^4);
for(int i=1;i<n;i++){//已经插入1~i,接下来要插入i+1这个数。
for(int j=0;j<=i*(i-1)/2;j++){//当前有j个逆序对。
for(int k=0;k<=i;k++){//枚举i+1要插入到哪里。
f[i+1][j+i-k]+=f[i][j];
}
}
}
//O(n^2)
for(int i=1;i<n;i++){//已经插入1~i,接下来要插入i+1这个数。
for(int j=0;j<=1;j++){//因为我们只在意它的奇偶性。
for(int k=0;k<=i;k++){//枚举i+1要插入到哪里。
f[i+1][(j+i-k)&1]+=f[i][j];
}
}
}
2.激动值
- 题目:激动,比它前面的都大,排列的激动值定义为有多少个激动的数,1到
的所有排列中,激动值为
的有多少个。
- 状态:
表示
到
已插入,激动值为
,
为方案数。
- 转移:首先发现从小到大插入,如果插入
,可能会导致激动值减小,所以应从大到小插入。我们发现只有当
插入到最前面时才会导致激动值加1,否则不产生变化。即插到最前面为
若不插到最前面则为
。
三、树形DP
当你有一个疑问,一个树有多少个点时,你肯定会快速想出来,有个点呀!但我们可以设计一个状态为
,这是什么?
这是玄学由子节点带来的数据来更新根节点。这样我们就可以dfs一下了,到叶节点时初始化,递归更新,根节点要再加一个点,更新数据。
如果给一棵个点的树,每条边都有长度,求
,即所有任意两点之间距离最大多少。你会发现每两个点都会有一个公共祖先,那么我们是不是可以设一个节点为公共祖先,求距此点最远的和次远的两个子节点,那过一个节点过这个公共祖先到另一节点即为较长,但这是较长,所以我们还要,取所有节点的这样两个点求路径,再取最大值。
如果给一棵个点树,求此树的最大独立集,最大独立集指的是选出尽量多的点,使得它们不相邻没有连边(不是没有联通的路径),我们可以设状态
,
,
即为选
,
即为不选,在简单的开始计数即可。
四、状压DP
当你做题时遇到感觉将状态中设置多个数很不方便时,且数量不多,你可以将这些数压缩,有、
、
、
,这四个数,你可以将它们变为
一个数,变为一个维度。常用的如,将一些内容用二进制压缩,如有
这样一个小区间,如果想要表示遍历过
、
、
,可以设状态为
(即
、
、
的位置为
)再转变为十进制即为
,这就是新的状态。
990

被折叠的 条评论
为什么被折叠?



