区间DP
kuangbin专题区间dp,应该每道题都有了,小标题名字都是自己看着方便瞎起的。
卡特兰分法
多边形切割
ZOJ-3537
将多边形可按卡特兰数分法分割,以
i
,
j
i,j
i,j为顶点分割线有代价
f
(
i
,
j
)
f(i,j)
f(i,j),求最小分法。
初始化 d p [ i ] [ i + 1 ] = c o s t [ i ] [ i + 1 ] = 0 dp[i][i+1]=cost[i][i+1]=0 dp[i][i+1]=cost[i][i+1]=0记忆化搜索即可。 O ( n 3 ) O(n^3) O(n3)
ll co(int i, int j){
if(c[i][j]!=-1) return c[i][j];
node a=h[i], b=h[j];
return c[i][j]=Abs(a.x+b.x)*Abs(a.y+b.y)%m;
}
ll sol(int i, int j){
if(dp[i][j]!=-1) return dp[i][j];
ll res=inf;
for(int k=i+1; k<j; k++){
res=min(res, sol(i, k)+co(i, k)+sol(k, j)+co(k, j));
}
return dp[i][j]=res;
}
栈的进出
LightOJ-1422
给定每个时刻栈顶元素,求最小输入数据个数。
初始化 d p [ i ] [ i ] = 1 dp[i][i]=1 dp[i][i]=1, d p [ i + 1 ] [ i ] = 0 dp[i+1][i]=0 dp[i+1][i]=0可不写, d p [ i ] [ j ] = d p [ i ] [ j − 1 ] dp[i][j]=dp[i][j-1] dp[i][j]=dp[i][j−1](选新的), d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k + 1 ] [ j − 1 ] , { k ∣ a [ k ] = = a [ j ] & & i < = k < j } dp[i][j]=dp[i][k]+dp[k+1][j-1],\ \{k\mid a[k]==a[j]\ \&\&\ i<=k<j\} dp[i][j]=dp[i][k]+dp[k+1][j−1], {k∣a[k]==a[j] && i<=k<j}(用以前的)。 O ( n 3 ) O(n^3) O(n3)
int sol(int i, int j){
if(dp[i][j]!=-1) return dp[i][j];
int res=sol(i, j-1)+1;
if(a[j]==a[j-1]) res=min(res, dp[i][j-1]);
for(int k=i; k<=j-2; k++){
if(a[k]==a[j])
res=min(res, sol(i, k)+sol(k+1, j-1));
}
return dp[i][j]=res;
}
括号配对
POJ-2955
CF-149D
s应为: ab 或 (a)。
不连续:即())()=4。
其中一种方法:
d p [ i ] [ j ] = d p [ i + 1 ] [ j − 1 ] + 2 , { a [ i ] = = ′ ( ′ & & a [ j ] = = ′ ) ′ } dp[i][j]=dp[i+1][j-1]+2,\ \{a[i]=='(' \&\& a[j]==')'\} dp[i][j]=dp[i+1][j−1]+2, {a[i]==′(′&&a[j]==′)′}, d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k + 1 ] [ j ] , { k ∣ i < = k , k + 1 < = j } dp[i][j]=dp[i][k]+dp[k+1][j],\ \{k\mid i<=k, k+1<=j\} dp[i][j]=dp[i][k]+dp[k+1][j], {k∣i<=k,k+1<=j}
取最小值即可。
连续:即())()=2。(没有题目提交过,不保证正确),用栈或者:
d p [ i ] [ j ] = 1 , { a [ i ] = = ′ ( ′ & & a [ j ] = = ′ ) ′ & & d p [ i + 1 ] [ j − 1 ] } dp[i][j]=1,\ \{a[i]=='(' \&\& a[j]==')' \&\& dp[i+1][j-1]\} dp[i][j]=1, {a[i]==′(′&&a[j]==′)′&&dp[i+1][j−1]}, d p [ i ] [ j ] = 1 , { d p [ i ] [ k ] & & d p [ k + 1 ] [ j ] & & i < k , k + 1 < j } dp[i][j]=1,\ \{dp[i][k]\&\&dp[k+1][j]\&\&i<k,\ k+1<j\} dp[i][j]=1, {dp[i][k]&&dp[k+1][j]&&i<k, k+1<j}
数列取数
数列两端取数
d p [ i ] [ j ] = F ( f ( d p [ i + 1 ] [ j ] , a [ i ] ) , f ( d p [ i ] [ j − 1 ] , a [ j ] ) ) dp[i][j]=F(f(dp[i+1][j],\ a[i]),\ f(dp[i][j-1],\ a[j])) dp[i][j]=F(f(dp[i+1][j], a[i]), f(dp[i][j−1], a[j]))
//eg:score=cnt(选取顺序)*a[i],求maxn(score),不用初始化
for(int i=n; i>=1; i--){
for(int j=i; j<=n; j++){
dp[i][j]=max(dp[i+1][j]+a[i]*(n-(j-i)),
dp[i][j-1]+a[j]*(n-(j-i)));
}
}
数列中间取数
POJ-1651
1,n最后取,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为去完
i
,
j
i,j
i,j中间的数最小或最大代价。
d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k ] [ j ] + f [ i , k , j ] dp[i][j]=dp[i][k]+dp[k][j]+f[i,k,j] dp[i][j]=dp[i][k]+dp[k][j]+f[i,k,j],最后取 k k k。
int sol(int i, int j){
if(dp[i][j]!=-1) return dp[i][j];
if(i+1==j) return dp[i][j]=0;
int res=(int)inf;
for(int k=i+1; k<j; k++){
res=min(res, sol(i, k)+sol(k, j)+a[i]*a[k]*a[j]);
}
return dp[i][j]=res;
}
可入栈的取数
HDU-4283
已有顺序1到n,和代价a[i],个人代价为前面出场人数乘a[i],有一空栈可用于调整顺序。
//错误:没有考虑中间出场
int solwr(int i, int j){
if(dp[i][j]!=-1) return dp[i][j];
if(i>=j) return dp[i][j]=0;
return dp[i][j]=min(s[j]-s[i], a[i]*(j-(i+1)+1))+solwr(i+1, j);
}
int sol(int i, int j){
if(dp[i][j]!=-1) return dp[i][j];
if(i>=j) return dp[i][j]=0;
int res=s[j]-s[i]+sol(i+1, j);
for(int k=i+1; k<=j; k++){
res=min(res, sol(i+1, k)+a[i]*(k-(i+1)+1)+
(s[j]-s[k])*(k-i+1)+sol(k+1, j));
}
return dp[i][j]=res;
}
段覆盖
覆盖空字符串
每次可将 a a a的一连续段改为某一字符,求将空字符串 a a a修改为 s s s的最小修改次数。
没有重复字符: d p [ j ] [ i ] = d p [ j + 1 ] [ i ] + 1 = d p [ j ] [ k ] + d p [ k + 1 ] [ i ] dp[j][i]=dp[j+1][i]+1=dp[j][k]+dp[k+1][i] dp[j][i]=dp[j+1][i]+1=dp[j][k]+dp[k+1][i]
显然,必须采取先覆盖两端边界为最优解,故:
s [ j ] = = s [ i ] : d p [ j ] [ i ] = d p [ j + 1 ] [ i ] = d p [ j ] [ i − 1 ] s[j]==s[i]:\ dp[j][i]=dp[j+1][i]=dp[j][i-1] s[j]==s[i]: dp[j][i]=dp[j+1][i]=dp[j][i−1]
这里固定 i i i即可计算。
memset(dp, 0, sizeof(dp));
for(int i=0; i<n; i++){
for(int j=i; j>=0; j--){
dp[j][i]=dp[j+1][i]+1;
for(int k=j+1; k<=i; k++){
if(s[j]==s[k])
dp[j][i]=min(dp[j+1][k]+dp[k+1][i], dp[j][i]);
}
}
}
覆盖非空字符串
HDU-2476
先计算空字符串,
S
[
i
]
=
m
i
n
(
d
p
[
0
]
[
i
]
,
S
[
i
−
1
]
)
+
1
S[i]=min(dp[0][i],\ S[i-1])+1
S[i]=min(dp[0][i], S[i−1])+1。
a [ k ] = = s [ k ] : S [ i ] = m i n ( S [ i ] , S [ k − 1 ] + d p [ k + 1 ] [ i ] ) a[k]==s[k]:\ S[i]=min(S[i],\ S[k-1]+dp[k+1][i]) a[k]==s[k]: S[i]=min(S[i], S[k−1]+dp[k+1][i])
S[0]=(a[0]==s[0]?0:1);
for(int i=1; i<n; i++){
S[i]=min(dp[0][i], S[i-1]+1);
for(int k=0; k<=i; k++){
if(a[k]==s[k]) S[i]=min(S[i], S[k-1]+dp[k+1][i]);
}
}