今天做到了一道题,一道区间dp,和上次的那道题挺类似的,想归类一下这类题型。
这道题是2020的台北的e题。
题目大意:给定一个由7种字母组成的序列,每次可以消去长度大于等于 m m m的连续相同字符串,然后其他字符再连接到一起(有点类似消消乐)。问是否能够消完 ( n , m < = 500 ) (n,m<=500) (n,m<=500)
思考:本题和那道题很像,那道题使用的是区间dp的手段,于是思考这道题能不能使用区间dp?
首先发现一个性质:一段连续的字符串,能消掉则一定全部消掉。因为这样不会使得答案变劣。于是一段连续区间可以缩点。
这里我们使用 d p [ l ] [ r ] [ 0 / 1 ] dp[l][r][0/1] dp[l][r][0/1]代表区间 [ l , r ] [l,r] [l,r]中, d p [ 0 ] dp[0] dp[0]能不能完全消掉 [ l , r ] [l,r] [l,r]的字符串(能消掉的dp值为1,不能则为0), d p [ 1 ] dp[1] dp[1]则是 l l l点与 r r r点的颜色相同的情况下,能不能消得区间 [ l , r ] [l,r] [l,r]变成只剩下 l l l点的颜色,且满足剩余个数小于 m m m个(值同上)。
那么对 d p dp dp部分:
For(i, 1, n){
if(a[i].cnt >= m) dp[i][i][0] = 1;
else dp[i][i][1] = 1, sum[i][i] = a[i].cnt;
}
For(i, 2, n){
For(l, 1, n - i + 1){
int r = l + i - 1;
int p = a[l].c == a[r].c;
For(k, l, r - 1){
dp[l][r][0] = max(dp[l][r][0], dp[l][k][0] & dp[k + 1][r][0]);
}
if(p){
if(dp[l + 1][r - 1][0]){
sum[l][r] = a[l].cnt + a[r].cnt;
if(sum[l][r] >= m) dp[l][r][0] = 1;
else dp[l][r][1] = 1;
}
For(k, l + 1, r - 1){
if(a[k].c == a[l].c && ((dp[l][k][0] | dp[l][k][1]) & dp[k + 1][r - 1][0])){
sum[l][r] = max(sum[l][r], sum[l][k] + a[r].cnt);
if(sum[l][r] >= m) dp[l][r][0] = 1;
else dp[l][r][1] = 1;
}
}
}
//cout<<l<<" "<<r<<" "<<dp[l][r][0]<<" "<<dp[l][r][1]<<" "<<sum[l][r]<<endl;
}
}
d
p
[
l
]
[
r
]
[
0
]
dp[l][r][0]
dp[l][r][0]可以直接由中间的断点
k
k
k转移:
d
p
[
l
]
[
r
]
[
0
]
=
m
a
x
(
d
p
[
l
]
[
k
]
[
0
]
&
d
p
[
k
+
1
]
[
r
]
[
0
]
)
dp[l][r][0]=max(dp[l][k][0] \ \& \ dp[k+1][r][0])
dp[l][r][0]=max(dp[l][k][0] & dp[k+1][r][0])
而
d
p
[
l
]
[
r
]
[
1
]
dp[l][r][1]
dp[l][r][1]则可以直接由
l
l
l和
r
r
r点相等直接转移,或者由中间的某个相同的点转移。(详情见代码)
中间点转移,其实是将那些不能单独消去的收到一块来,然后一起消去。这里需要用一个
s
u
m
[
l
]
[
r
]
sum[l][r]
sum[l][r]统计现在最多能收留了多少个
a
[
l
]
.
c
a[l].c
a[l].c不能单独消去的(满足
l
l
l和
r
r
r点的颜色相同)。