题意:给你一个n值一个k值,一个长度为n的01串,现在要求你切割原串,使新形成的每个子串都满足,长度不超过k,且任何子串都不是完全交替的01串,利于010101就是完全交替的01串,而00101010就不是,问你最少需要切几刀。
题解:完全照着官方题解来的,自己看了几个小时也没看明白,问巨巨们都是扫一眼就告诉我怎么做,然而即便是这样我还是无法理解每一步程序究竟是什么意思,后来跟phy讨论了半天,慢慢慢慢才搞懂了。
虽然这貌似在大神们眼里是个很简单的dp,但是既然卡了我这么久,那还是好好理下思路。
首先,题解里的外循环i是从后往前推的,然后内循环是从第i位向后推k位。当然这两个循环是完全可以调头的(i从前往后,内循环从i往前推k位)这些都无所谓。
dp[i]代表的是从第i位开始往后的串在满足条件的情况下切完后所形成的子串数目。
下面就开始讲解dp的递推过程。
假设一个串11100,k为3,那么我们从右往左推首先初始化dp[5]=0,因为s下标从0到n-1,所以相当于第5位没东西,自然没有一个串。
接下来进入循环。
I=4,则j从4开始到min(s.len,i+l-1)即第4位,当i==j时,dp[4]=dp[5]+1;
那么这个dp[5]+1是什么意思呢,我们知道dp[5]=0代表第五位没有串。那么dp[5]+1即,在第五位的基础上加了一个串进去,也就是s[4]。那么此时dp[4]=1就代表有了一个合法的串。
I=3,则j从3开始到min(s.len,i+l-1)即第4位,先是dp[3]=dp[4]+1=2,即在段s[4]外加了一个段s[3].
就是这样 0 0,红色的零就是我们当前加进去的串。然后当j=4时,因为s[j]=s[j-1]=0,所以,s[j]与s[j-1]所构成的串为合法串,那么它们俩就可以单独构成一个串。也就是说可以合起来放在第5位外面,即0 0,这样dp[i]就可以取1。取最优解。
I=2时,j从2到4,同样dp[2]=dp[3]+1=2,代表这样,1 0 0,而当j=3时,因为s[j]!=s[j-1],所以从s[j]与s[j-1]所构成的串是不合法的,所以我们不能把他们俩组合起来加在第4位的外面(也就是这样1 0 0),所以不进行比较,而当j=4时,因为s[j]==s[j-1]=0,此时dp[i]=min(dp[i],dp[j+1]+1)也就是把100连起来放在了第5位外面,拿这种情况(100)跟(100)相比,取最优解(100)dp[2]=1;
以下红色都为当前i到j之间的串。
I=1时,
J=1先是这样1 1 0. . .
J=2 11 0 . . .
J=3 1 1 0 . . .(因为前面出现过11,所以0可以加在11后面形成合法串)
取最优解 1 1 0 . . .
I=0时
J = 0 1 1 1 . . .
J = 1 1 1 1 ....
J= 2 1 1 1 ....
取最优解
那么最后dp[0]即为最少段数,dp[0]-1就是最少切割次数。
果然,蒟蒻就要多写写尽量写完整才能把思路理清。
代码(其实就是官方题解):
#include<bits/stdc++.h>
#define MEM(a,x) memset(a,x,sizeof(a));
#define MEMINF(a) memset(a,0x3f,sizeof(a));
using namespace std;
typedef long long LL;
const int MAXN=205;
const int INF=0x3f3f3f3f;
const int MOD=1000000007;
int dp[MAXN];
int main() {
int T;
cin>>T;
while (T--) {
int n,k;
string s;
cin>>n>>k>>s;
dp[n]=0;
for (int i=s.size()-1; i>=0; i--) {
bool flag=true;
dp[i]=INF;
for (int j = i; j <= i + k - 1 && j < s.size(); ++j) {
if ( j > i && s[j] == s[j - 1])
flag = false;
if (i == j || flag == false )
dp[i]= min(dp[i], 1 + dp[j + 1]);
}
}
printf("%d\n",dp[0]-1);
}
}