动态规划及其优化
Posted solemntee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划及其优化相关的知识,希望对你有一定的参考价值。
(转移)和结尾元素有关的dp
1、只最后一个元素相关
问题1:最长上升连续子串(连续区间 d p dp dp)
对于连续区间之间的
d
p
dp
dp,转移的对象极为清晰,不多赘述
设置
d
p
[
i
]
dp[i]
dp[i]表示以
a
[
i
]
a[i]
a[i]为右端点的最长上升子串长度,如果
a
[
i
]
<
=
a
[
i
−
1
]
a[i]<=a[i-1]
a[i]<=a[i−1]则
d
p
[
i
]
=
0
dp[i]=0
dp[i]=0,否则
d
p
[
i
]
=
d
p
[
i
−
1
]
+
1
dp[i]=dp[i-1]+1
dp[i]=dp[i−1]+1
问题2:(子序列dp)
Meeting Time II
二维平面上有n个站点,你需要按顺序经过(从站点1到站点2到站点3…)这些站点,但你可以选择k个站点进行跳过,求最短路
设置 d p [ i ] [ j ] dp[i][j] dp[i][j]表示以 i i i节点作为右端点且已经跳跃了 j j j次的最短路,这样就可以通过枚举上一个节点 O ( n 3 ) O(n^3) O(n3)转移
(扯开去讲一下,因为区间之间的转移和最后一个元素是什么有关,所以对于每一个转移知道状态的结尾元素,设置以 i i i作为右端点是非常有必要的,如果这边状态中 i i i含义是前 i i i个的话就没办法转移了,因为对于 d p [ i ] [ 1 ] dp[i][1] dp[i][1]你不知道跳过了哪个节点。但有的时候对于子序列计数问题,我们会将状态设置为前 i i i个的数量前缀和,因为这个时候就我们不需要知道最后一个元素具体位置,只需要知道某结尾序列数量即可)
问题3、最长上升子序列(子序列dp的优化)
同样是寻找上一个端点,不同于 O ( n ) O(n) O(n)暴力的寻找,在 L I S LIS LIS里面我们通过二分优化了寻找上一个元素的时间复杂度
2、只和最后k个元素相关
考虑状压最后 k k k个元素的形态,花 O ( 2 k ) O(2^k) O(2k)的复杂度进行转移
3、只和最后一个连续段长度相关,且间隔(gap)固定
(间隔不定的话就只能二进制枚举了)
总体来讲思路都是极为相似的,通过设置
d
p
dp
dp状态为右端点来进行转移
设置
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为以
i
i
i作为右端点的,最后一个连续段长度为
j
j
j的
d
p
dp
dp值,考虑转移
d
p
[
i
+
g
a
p
+
k
]
[
k
]
=
f
(
d
p
[
i
]
[
j
]
,
k
)
dp[i+gap+k][k]=f(dp[i][j],k)
dp[i+gap+k][k]=f(dp[i][j],k)
前缀和优化dp
Armchairs
题目的意思是有
n
n
n个座位
m
m
m个人
(
m
<
n
/
2
)
(m<n/2)
(m<n/2)坐在一些位置上,现在要求原来这些坐着人的位置都空出来,让这些人坐在其他座位上,问移动距离总和的最小值。
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示第i个人坐到第j个原来空着的位置,并且前i个人完全匹配的最小移动距离。
我们来考虑转移
d
p
[
i
]
[
j
]
=
m
i
n
k
=
1
,
2
,
.
.
,
j
−
1
(
d
p
[
i
−
1
]
[
k
]
)
+
a
b
s
(
d
i
s
1
[
i
]
−
d
i
s
0
[
j
]
)
dp[i][j]=min_{k=1,2,..,j-1}(dp[i-1][k])+abs(dis1[i]-dis0[j])
dp[i][j]=mink=1,2,..,j−1(dp[i−1][k])+abs(dis1[i]−dis0[j])
我们想到了一个O(n^3)的dp,然后我们发现
m
i
n
k
=
1
,
2
,
.
.
,
j
−
1
(
d
p
[
i
−
1
]
[
k
]
)
min_{k=1,2,..,j-1}(dp[i-1][k])
mink=1,2,..,j−1(dp[i−1][k])
是可以
O
(
n
)
O(n)
O(n)预处理的,那么这个
d
p
dp
dp就被优化掉了一维,变成了
O
(
n
2
)
O(n^2)
O(n2)
#include<bits/stdc++.h>
using namespace std;
int cnt1=0,cnt0=0;
int dis1[5005],dis0[5005];
int dp[5005][5005];
int minn[5005];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
if(t==1)dis1[++cnt1]=i;
if(t==0)dis0[++cnt0]=i;
}
for(int i=0;i<=cnt1;i++)
for(int j=0;j<=cnt0;j++)
dp[i][j]=1e9;
for(int i=1;i<=cnt0;i++)dp[1][i]=abs(dis1[1]-dis0[i]);
for(int j=0;j<=cnt0;j++)minn[j]=1e9;
for(int j=1;j<=cnt0;j++)minn[j]=min(minn[j-1],dp[1][j]);
for(int i=2;i<=cnt1;i++)
{
for(int j=i;j<=cnt0;j++)if(minn[j-1]!=1e9)dp[i][j]=minn[j-1]+abs(dis1[i]-dis0[j]);
for(int j=0;j<=cnt0;j++)minn[j]=1e9;
for(int j=1;j<=cnt0;j++)minn[j]=min(minn[j-1],dp[i][j]);
}
int minn=1e9;
if(cnt1==0)printf("0\\n");
else
{
for(int i=1;i<=cnt0;i++)
{
minn=min(minn,dp[cnt1][i]);
}
printf("%d\\n",minn);
}
return 0;
}
Matrix 以上是关于动态规划及其优化的主要内容,如果未能解决你的问题,请参考以下文章
题目的意思是在这个矩阵里面选取一块楼梯形的区域,使得区域和最大。
我们设状态
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k],
i
i
i表示第
i
i
i列,
j
j
j表示第
j
j
j列的高度,
k
k
k表示总总共选取的方格数。
考虑转移
d
p
[
i
]
[
j
]
[
k
]
=
m
a
x
t
>
=
j
(
d
p
[
i
−
1
]
[
t
]
[
k
−
j
]
)
dp[i][j][k]=max_{t>=j}(dp[i-1][t][k-j])
dp[i][j][k]=maxt>=j(dp[i−1][t][k−j])
复杂度为
O
(
n
4
)
O(n^4)
O(n4)
然后我们发现这个
m
a
x
max
max值满足递推关系
m
a
x
t
>
=
j
+
1
(
d
p
[
i
−
1
]
[
t
]
[
k
−
j
]
)
=
m
a
x
(
m
a
x
t
>
=
j
(
d
p
[
i
−
1
]
[
t
]
[
k
−
j
]
)
,
d
p
[
i
−
1
]
[
j
]
[
k
−
j
]
)
max_{t>=j+1}(dp[i-1][t][k-j])=max(max_{t>=j}(dp[i-1][t][k-j]),dp[i-1][j][k-j])
maxt>=j+1(dp[i−1][t][k−j])=max(maxt>=j(dp