题目描述
输入输出样例
题目理解
题目要求我们在改变一个0为1的情况下,问最多能将原01串变为多少个长度为k的全1子串
首先我们要弄清这一个1最多能产生多大的影响:
因为分割的子串是不相交的,假设某个0的分割效果最“明显”(也就是把它变为1效益最大)
它的左边在划分完所有可分为k长度的串之后,剩余k-1个1,右边同样有k-1个1,那将它变为1后,最多能让结果多几个k长的子串呢?
答案是1个,因为 k <k-1 + k-1 + 1<2k
既然如此,我们只要考虑什么情况下,我们可以在结果上加1即可
情况分析
首先,毫无疑问,我们是采用遍历原01序列的办法统计每个被0分隔的全1子串的长度
某个0的左边和右边相加要大于等于k-1,才能满足在结果上可加1的条件
如何得到每个0左右的长度就是关键
在我们采用遍历统计的办法下,我们统计的目前长度记为cur,它可以表示成当前这个0的左边
但同时也可以表示成前一个0的右边,那么我们只需要多一个变量统计前一个0的左边即可
(因为现在这个0的右边,在我们下一次遇到0时便会被讨论进来)
这个变量记为before
那么我们每次遇到0时,开始讨论的格局应该如下所示
11111111.... 0 11111111111... 0 11111...
↑长度为before的子串 ↑长度为cur的子串 ↑当前位置
那么这道题最关键的部分代码也就可以写出来了
不要忘记:有0,才能有0变1的操作,0都没有的话何谈加1!
char fig[10010];
int a, k;
int solve() {
bool f = false,z = false;//加一标志 ,有零标志
int cur = 0, sum = 0, before = 0;
for (int i = 0; fig[i] != '\0'; i++) {
if (fig[i] == '0') {
z = true;
if (cur >= k)sum+=cur/k;
//划分后剩下的其实就是对k取模
if((before+cur%k)>=k-1)f = true;
//可供下一次讨论利用的值也只有取模剩下的部分
before = cur%k;
cur = 0;
} else cur++;
}
//如果序列不以0结尾,仍然要把统计的cur与before处理到结果当中
if(cur)sum+=cur/k;
if((!f)&&(cur%k+before>=k-1))f = true;
//有0且满足加1条件,则返回的结果+1;否则返回原结果
if(f&&z)return sum+1;
return sum;
}
完整code
#include<iostream>
using namespace std;
char fig[10010];
int a, k;
int solve() {
bool f = false,z = false;
int cur = 0, sum = 0, before = 0;
for (int i = 0; fig[i] != '\0'; i++) {
if (fig[i] == '0') {
z = true;
if (cur >= k)sum+=cur/k;
if((before+cur%k)>=k-1)f = true;
before = cur%k;
cur = 0;
} else cur++;
}
if(cur)sum+=cur/k;
if((!f)&&(cur%k+before>=k-1))f = true;
if(f&&z)return sum+1;
return sum;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &a, &k);
scanf("%s", fig);
printf("%d\n", solve());
}
}