DP
DP
解题步骤
1 设计\(dp\)状态
2 求状态转移方程
3 初始化必要的简单状态
复杂度分析 : 状态数量*每个状态的转移次数复杂度 或 所有转移复杂度之和
走楼梯
\(设计状态\) $dp_i $: 走到第i阶的方案数
\(根据状态求出状态转移方程\) \(dp_i = dp_{i-1}+dp_{i-2}\)
\(初始化:dp_0 = 1\)
走k层
每一步可以走\(1,2,..k\)层,求方案数
\(1<n*k\leq 10^5\)
状态和上面一样设计。
转移方程为\(dp_i = \sum_{j=1}^{k}{dp_{i-j}}\)
时间复杂度\(O(nk)\)
数学优化
\(dp_{i} = dp_{i-1}+dp_{i-2}+dp_{i-3}+...+dp_{i-k}\)
\(dp_{i-1} = dp_{i-2}+dp_{i-3}+dp_{i-4}+...+dp_{i-k-1}\)
相减得 \(dp_i=2*dp_{i-1}-dp_{i-k-1}\)
时间复杂度\(O(n)\)
PS: 一些简单的递推式 可以用 矩阵快速幂优化 复杂度与\(logn\)相关 矩阵加速
一些\(dp\)求出来的递推式还可以用高中数列学过的特征根方程解出来得到\(O(1)的式子\)
\(eg\). 长度为n的环,给每一个珠子染色,可以染m中颜色,需要保证相邻珠子不同颜色,求方案数. \(n\leq10^9,m\leq10^9\)
可以\(dp\)+矩阵加速。可以推\(O(1)式子\)
前缀和优化
设 \(pre_i = \sum_{j=1}^{j=i}{dp_j}\)
\(dp_i = pre_{i}-pre_{i-k-1}\)
时间复杂度 \(O(n)\)
最长公共子序列
\(状态:f_{i,j} : 串1中以i结尾,和串2中以j结尾的最长公共子序列\)
转移方程
初始化\(f_{i,j}=0\)
最长上升子序列
状态: \(dp_{i}:\)以i结尾的最长上升子序列
\(转移方程 :dp_i = max_{j=1,a_{j}<a_{i}}^{i-1}dp_{j}+1\)
初始化:\(dp_{i} = 1\)
复杂度\(O(n^2)\)
rep(i,n)
{
cin>>a[i];
}
int maxn;
for(int i=1;i<=n;i++)
{
dp[i]=1;
for(int j=1;j<=i;j++) {
if (a[j] < a[i])
dp[i] = max(dp[i],dp[j] + 1);
maxn=max(maxn,dp[i]);
}
}
cout<<maxn;
优化
贪心算法
for(int i=0;i<n;i++)
{
cin>>a[i];
}
memset(dp,0x1f,sizeof(dp));
int mx=dp[0];
for(int i=0;i<n;i++)
{
*upper_bound(dp,dp+n,a[i])=a[i];
}
int ans=0;
while(dp[ans]!=mx)
ans++;
cout<<ans;
数据结构优化
\(转移方程 :dp_i = max_{j=1,a_{j}<a_{i}}^{i-1}dp_{j}+1\) 中 $ max_{j=1,a_{j}<a_{i}}^{i-1}dp_{j}+1 ,可以用线段树或者树状数组求一个前缀最大值,并支持单点修改$
时间复杂度\(O(nlogn)\)
记忆化搜索
把所有状态都看成一个点,转移关系用有向边连接,若得到一个DAG图则可以用dp记忆化搜索
题目
[USACO1.5] [IOI1994]数字三角形 Number Triangles
SG函数
背包DP
w v
1 3 2
2 4 2
3 7 4
int dp[i][j];
dp[1][6]=2;
i =2 j=6;
dp[i][j] = max{dp[i-1][j], dp[i-1][j-w[i]]+v[i]};
dp[n][W];
dp[2][6] = max{dp[1][6], dp[1][6-4]+2}
0-1 背包
完全背包
多重背包
区间DP
状态:\(dp_{i,j}合并区间[i,j]的最小值\)
转移:\(dp_{i,j} = min_{i\leq k<j}~~~{dp_{i,k}+dp_{k+1,j}+sum_{i,j}},({sum_{i,j}区间[i,j]的石子数量之和)}\)
初始化\(dp_{i,i}=a_{i}\)
复杂度\(O(n^3)\)
问题:怎么设计递推顺序求出所有\(dp_{i,j}\)?
问题:记忆化搜索如何设计?
树形DP
设计状态:$dp_{u,0/1} :对于以u为根的子树,选或则不选(0/1)u结点的最大值 $
转移方程\(dp_{u,0} = \sum_{v\in~son_{u}}{max{(dp_{u,0},dp_{u,1})}},dp_{u,1} = w_{u}+\sum_{v\in~son_{u}}{dp_{v,0}}\)
\(O(n)\)
树上背包
前置
滚动数组
举个例子求斐波拉数列
f[N]
f[1]=f[2]=1
for(int i=3;i<=n;i++){
f[i] = f[i-1]+f[i-2]
}
空间复杂度\(O(n)\)
优化:
int f1 = 1,f2 = 1
int f = 0
for(int i=3;i<=n;i++){
f = f1+f2;
f1 = f2;
f2 = f3;
}
空间复杂度\(O(1)\)
01背包优化
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
f[i][j] = f[i-1][j];
if(j>=v)f[i][j] = std::max(f[i][j],f[i-1][j-v]+w);
}
}
空间复杂度\(O(nv)\)
优化
int f[V];
for(int i=1;i<=n;i++){
for(int j=m;j>=v;j--){
f[j] = std::max(f[j],f[j-v]+w);
}
}
完全背包优化
优化
int f[v];
for(int i=1;i<=n;i++){
for(int j=v;j<=m;j++){
f[j] = std::max(f[j],f[j-v]+w);
}
}
空间复杂度\(O(V)\)
分组背包
即有n个组,每个组最多只能选其中一个
多重背包可以看成一个分组背包即:
例如(c,v,w) 数量为c个,体积为v,价值为w,那么第i组的物品为: \((v,w),(2*v, 2*w),...(c*v,c*w)\)
所以多重背包可以看成等差数列版的分组背包,这个性质就能用在单调队列的优化
多重背包搞明白了,分组背包也差不多啦

浙公网安备 33010602011771号