-
zhxのDP讲
DP基础例题
1. 斐波那契数列
f[1]=1;//第0项和第1项
f[0]=0;
//记忆化搜索
int dp(int n)//斐波那契数列第n项
{
//g[i]表示f[i]有没有计算过
if(n<=1)return n;
if(g[n])return f[n];
g[n]=true;
f[n]=dp(n-1)+dp(n-2);
return f[n];//O(fn)
}
//其它求当前
for(int i=2;i<=a;i++)
f[i]=f[i-1]+f[i-2];
//O(n)
//当前求其它
for(int i=0;i<=n;i++)
{
//O(n)
f[i+1]+=f[i];
f[i+2]+=f[i];
}
2. 组合数
for (int i=0;i<=n;i++)
{
C[i][0] = 1;//处理边界
for (int j=1;j<=i;j++)
C[i][j] = C[i-1][j-1] + C[i-1][j];
}
- IOI数字三角形及其系列
状态:f[位][置]=经过的数字之和
f[i][j]:走到 (i,j) 的数字最大和
//滑雪
n行m列
可以走任意四个方向
每走一步需要使得脚底数字变大
最多走几个格子
f[x][y]代表走到(x,y)的最长长度
若:a[x-1][y]>a[x][y];
f[x-1][y]=max(f[x-1][y],f[x][y]+1)
//不正确解,使用自己求其他
//但没有保证当前的位置求出
cin >> n >> m;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
cin >> a[i][j];
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
f[i][j]=1;
for (int x=1;x<=n;x++)
for (int y=1;y<=m;y++)
for (int d=上下左右)
{
int xx = x朝着d走;
int yy = y朝着d走;
if ( (xx,yy)存在 && a[xx][yy] > a[x][y]) f[xx][yy] = max(f[xx][yy],f[x][y]+1);
}
DP三要素:
- 状态
- 转移方程
- 边界条件
DP可加维度
f[i][j](二维)—f[i][j][k](三维)—f[i][j][k][l](四维)
若时间空间过高,削减冗余状态,减少维度
建一个和原来状态相同的数组储存方案或方案数
DP模型
背包DP
01背包
n个物品 背包容积为m
第i个物品体积为vi,价值为wi
需要向背包里装一定的物品,在体积不超限的情况下装价值最高的物品
f[编号][体积]=价值
f[i][j]表示考虑完前i个物品 背包已经使用了j的体积
//01背包
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
//v体积 w价值
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
//考虑求f[i][j]
f[i][j]=f[i-1][j];//第i个物品不选
if(j>=v[i])
f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i])
}
例题采药
无穷背包
n种物品 背包容积为m
每种物品数量不限
第i个物品体积为vi,价值为wi
f[i][j]表示考虑完前i种类物品 背包已经使用了j的体积
//无穷背包
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
//v体积 w价值
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
//考虑求f[i][j]
f[i][j]=f[i-1][j];//第i个物品不选
if(j>=v[i])
f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i])
}
例题疯狂的采药
有限背包(多重背包)
有N种物品和一个容量为T的背包,第i种物品最多有M[i]件可用,价值为P[i],体积为V[i]
求解:选哪些物品放入背包,可以使得这些物品的价值最大,并且体积总和不超过背包容量。
//有限(多重)背包
cin>>n>>m;
int k=0;
//v体积 w价值 u个数
for(int i=1;i<=n;i++)
{
int v_,w_,x_;
cin>>v_>>w_>>x_;
int r=1;
while(x_>=r)
{
k++;
v[k]=v_*r;
w[k]=w_*r;
x_-=r;
r*=2;
}
if(x_!=0){
k++;
v[k]=v_*x_;
w[k]=w_*x_;
}
}
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k*v[i]<=j&&k<=u[i];k++){
//考虑如何求f[i][j]
f[i][j]=max(f[i][j],f[i-1][j-kv[i]]+k*w[i]);
}
区间DP
区间DP特点:
- 状态设计前两维度一定为f[L][R];
- n<=100/200/500
- 只能对相邻的两个元素合并为一个
- 若区间为环形,将原数组在后面复制,组成长度为2n的数组,再进行区间DP
- 枚举分界点
1. 石子合并
f[l][r]表示区间[l,r]合并一堆的最小代价
//石子合并
//????[????][????]表示一段区间合并的最小代价
//枚举断点合并
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i];
f[i][i]=0;
}
for(int len=2;len<=n;len++)
for(int l=1,r=len;r<=n;l++,r++)
for(int k=l;k<r;k++)
f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+sum[r]-sum[l-1]);
2.能量项链
环形区间DP
3.括号序列
给出一个的只有()[]四种括号组成的字符串,
求最多能够选出多少个括号满足完全匹配
- 状态:为f[l][r] 代表区间[l,r]最多取出多少括号
- 边界:f[i][i]=0;
- 转移方程:
4.回文子串
求序列内有多少回文子串
两个回文子串只要位置不同就算做不同的回文子串
s[l]=s[r]
s[l+1]=s[r-1]
- 状态:f[l][r]表示l~r有多少回文子串
求回文子序列
- 状态:f[l][r]表示l~r有多少回文子序列
关键问题在于去重
转移一:容斥原理
转移二:当左右字符相同时 再补充一次内部答案
树形DP
前置知识
基于树的dp
- Dp方法始终为从下至上进行dp
- 在每个节点对所有儿子做聚合
- 可能需要多一遍dfs或者bfs
- 状态:[i]表示以i为根的子树有多少个点
- 边界条件以叶子节点来设置:f[叶子节点]=1;
- 转移方程:将所有儿子的信息合并得到根节点信息f[i]=f[p1]+f[p2]+····+f[pk]+i;
//树形DP
void dfs(int i){
if(i is yezi)
{
f[i]=1;
return ;
}
for(p 是 i的 儿子)
{
dfs(p);
f[i]+=f[p];
}
f[i]++;
}
Problem 1
询问树的最大独立集
n个点的树,
最多选几个点使得其互不相邻
- 状态:f[i][j]
i表示以i为根的子树最多选几个点
j表示当前的i点选或不选(0/1)
f[叶子][j]
类似例题 没有上司的舞会
Problem 2
现在要在一棵树上布置士兵,每个sb在结点上,每个sb可以守护其结点直接相连的全部边,问最少需要布置多少个sb
- 状态:f[i][j]以i为根的子树最少几个伞兵
i表示以i为根的子树
j取值为0/1/2
-
0:i点放了伞兵
-
1:i没放但是儿子放了
-
2:i没有放并且儿子都没有放
f[叶子][1]=无穷;
f[叶子][2]=0;
Problem 3
求树上最远的两个点的距离(求树的直径)
f[i][0]:i向下走最长的路
f[i][1]:i向下走次长的路
f[叶子][0/1]=0;
f[i][0]=1+max(f[p][0],f[i][0]);
Problem 4
树形01背包(依赖背包)
n个物品,背包容积为m,第i个物品体积为vi价值为wi,n个物品构成树,i进入背包前需要保证父亲已经进入背包
f[i][j]以i为根的子树,已经用了j的体积,能获得的最大价值之和
金明的预算方案
排列DP
前置
对于某个n!种排列中满足某个条件的排列有多少种
需要把1~n这些数按照顺序(小到大/大到小)顺序插入进去
Problem 1
1~n排列中有多少个排列的逆序对的数量是偶数
方程:
f[i+1][j+i-k]+=f[i][j];
但是!
此题可以不用DP
ans为(frac{n!}{2})
Problem 2
对于一个序列,定义其“激动值”为序列中严格大于前面所有数的
元素的个 数。比如,{1,1,5,6,5}的激动值为3。 给定n个数
p1,p2,...,pn,求这n个数的所有排列中,激动值不超过k的个数。
1 ≤ k ≤ n ≤ 200,1 ≤ pi ≤ 200
f[i][j]表示插入到i激动值为j的方案数
Problem 3
1~n的排列中,有多少最长上升子序列长度≤2
Problem 4
N个字符串,由数字组成,有n!种方法把它们连在一起,能得到n!个数,n!个数中有多少是11的倍。
∵奇位和-偶位和=11的倍数
∴这个数为11的倍数