DP总结
线性DP
-
定义:
具有线性"阶段"划分的被称为线性。
-
经典模型
, , (最长公共上升子序列) , , 。
-
例题:
-
LCS
题意:
求两个字符串中字典序最小的。
方法:
用 记录下每一个状态下的答案。
-
最长上升子序列(LIS)
题意:
- 给定一个长为 n 的序列 ,求这个序列的最长单调上升子序列长度。
- 要求 。
方法:
在长度一定的情况下,若结尾元素更小,那么最终答案也就更优。
所以我们维护一个数组表示当前的,每一次插入当前元素。
code :
using namespace std;
const int N=1e6 +3;
int n,m,a[N],f[N];
signed main()
{
cin>>n; for(int i=1;i<=n;i++) cin>>a[i];
memset(f,0x3f,sizeof f);
for(int i=1;i<=n;i++) *upper_bound(f+1,f+n+1,a[i])=a[i];
int res=1; while(f[res]!=f[0]) res++;
cout<<res-1<<endl;
return 0;
}
-
二维平面上的*P与 状态压缩DP
-
怎样判断?
二维平面看题面。
状态压缩的。
-
例题:
-
Grid 2
题目:
给一个 H×W 的网格,每一步只能向右或向下走,给出 个坐标,这些坐标对应的位置不能经过,求从左上角 (1,1)走到右下角 (H,W) 的方案数,答案对 取模。
思路:
首先对于 可行性显然,但空间不够。我们只能对着 个坐标开数组。
我们设方程 表示从 (1,1) 做到第i个坐标,且只经过第个坐标的方案总数。
那么,我们尝试使用容斥原理进行转移
- 从 (1,1) 到 () 的方案数是
- 从某一个坐标转移过来的方案数是 $\sum C_{x_i-x_j+y_i-y_j}^{x_i -x_j} $
-
Matching
题目:
给定 N,表示有 N 个男生和 N 个女生,再给你一个矩阵 a,如果 等于 1,表示 i 这个男生和 j 这个女生可以匹配成一对,否则不能。 问要匹配 N 对的方案数。答案对 取模。
思路:
状压男生,枚举女生
-
区间DP
都离不开三重循环:
-
石子合并
题目:
在一个圆形操场的四周摆放 N 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 N* 堆石子合并成 1 堆的最小得分和最大得分。
思路:
令
dp[i][j]
表示区间[i,j]
的最小价值。不妨从终点考虑问题,即结果为两个子区间合并的最小值再加上合并需要的代价即可。
枚举两个子区间,即枚举这个区间的中间点k,使这个区间被分为
[i,k]
和[k+1,j]
两个区间,取一遍最小值加上合并的即为当前区间所求。至于合并的代价,用前缀和即可。
所以
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
-
Deque
题目:
给定 N 个数的序列 。
A 和 B 轮流取数,每个人每次可以从序列头部或者尾部取走一个数(直到序列为空)。
A 和 B 都希望自己取得的数的总和尽可能大。
假设最优策略下,A 取得的数的总和是 X,B 取得的数的总和是 Y, 请输出 X-Y。
思路:
显然游戏过程中剩下的数必然是连续的一段。设 表示剩下下标为 的数时,先手(并非当前的先手而是开始时的先手,下同)能取得的最大分数差。
分两种情况讨论:
- 已经取走的数为偶数个,此时先手取,= (,,)
- 已经取走的数为奇数个,此时后手取,= (,,)
换根DP
本质上只是把序列变成了树
-
Subtree
题目:
有一个 n 个节点的树,对一些节点染色,使得所有被染色的节点是一个连通块。求对于 每个节点,该节点被染色的方案个数。所有答案对 M 取模。
思路:
我们设表示以为跟的子树中的可行方案数,表示的父节点中除了的方案数。
那么,有。
DP优化
根据方程运用相应的数据结构即可。