题目来源
题目分析
题目的意思就是求出最大的切割次数是多少,切割的条件图片中描述的很清楚。就是切割的左部分的0的个数与右部分1的个数的差值的绝对值要在
[
L
,
R
]
[L,R]
[L,R]之间。
思路
一开始是想着用贪心的方法来写,但是没有写出来,贪心不能够将所有的条件都给考虑到。因为切割之后的串还可以继续进行操作。大部分可以生出小部分。这就符合DP的思想。虽然当时感觉DP是正解,但是状态转移方程不会写,一点DP的思路都没有。
这是一个线性DP的题。我们设
f
[
L
]
[
R
]
f[L][R]
f[L][R]代表的意思是从L到R的最大切割次数。
之后来推状态转移方程,我们发现在L到R之间如果有一个K将这一段分成两段,并且又正好符合切割条件,那么
f
[
L
]
[
R
]
=
f
[
L
]
[
K
]
+
f
[
K
+
1
]
[
R
]
+
1
f[L][R]=f[L][K]+f[K+1][R]+1
f[L][R]=f[L][K]+f[K+1][R]+1,我们用一重循环来循环L的值,用一重循环来循环R的值,再用一重循环来循环K的值,然后要想判断是否符合题目中的条件就要每次循环找一下0和1的个数,我们这里如果再开一重循环就是
n
4
n^4
n4,一定会超时的,所以我们用前缀和预处理一下。就能将
n
4
n^4
n4降为
n
3
n^3
n3
下面看一下代码
代码
int a[1050],b[1050];
void solve () {
int n,l,r;cin>>n>>l>>r;
string s;cin>>s;s = " "+s;
for (int i=1;i<=n;i++) {
a[i]=a[i-1]+(s[i]=='1');
b[i]=b[i-1]+(s[i]=='0');
}
for (int i=1;i<=n;i++) {
for (int q=1;q+i-1<=n;q++) {
int w=q+i-1;
for (int k=q;k<=w-1;k++) {
int x=b[k]-b[q-1];
int y=a[w]-a[k];
if (abs(x-y)>=l&&abs(x-y)<=r)
f[q][w]=max(f[q][w],f[q][k]+f[k+1][w]+1);
}
}
}
cout<<f[1][n]<<'\n';
}
总结
感觉是一道比较好的线性DP的题,不是很简单感觉。感觉对于没怎么学过动态规划的人来说,这一题还是比较难的,只能多刷刷题,早日能自己写出来这种题