题目
有一个由0和1组成的长为n的字符串,其中有m个字符是1,对于一个长度恰好为3的子区间,如果字符1的数量比0大,就是一个坏区间,你可以自由调整0和1的位置,但数量不能改变,求坏区间总数最少的字符串中有几个坏区间 。
思路
如果我们贪心地想不让坏区间出现,我们就能构造出一个100100100100……由100为一个单位组成的字符串,如果这时候m个1能全部放完,答案自然就是0,如果不能放完,我们要想办法将字符串里的某些0替换成1,使得增加的坏区间最少。
再继续分析,对于一个0变成一个1,那么受影响的自然是以这个数字为开头,为中间,为末尾的三个子区间。因此我们先操作右边界和左边界的0,因为左边界的0不存在以它为末尾的长度恰好为3的子区间,右边界的0不存在以它为开头、以它为中间的长度恰好为3的子区间。剩下的只要从左到右依次地将0改为1就可以了,因为相靠的两个0变成1会产生重合的受影响的子区间。对于具体计算,我们发现从左到右依次将0改成1,对于一个“100”单元,由于前一个单元的影响,第一个0会增加两个坏区间(以它为中间,以它为开头),由于第一个0的影响,第二个0会增加一个坏区间(以它为开头)。然后问题就解决了。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1005;
int num[2]={2,1};
char str[maxn];
int main(){
int n,m;
cin>>n>>m;
if(n==m&&n>=3){
cout<<n-2;
return 0;
}
else if(n<3){
cout<<"0";
return 0;
}
for(int i=0;i<n;i++){
if(i%3==0){
str[i]='1';
m--;
}
}
if(m<=0){
cout<<"0"<<endl;
return 0;
}
int ans=0,flag=0;
for(int i=n-1;i>=0;i--){
if(str[i]=='1'||m<=0)break;
str[i]='1';
m--;
ans++;
}
if(m<=0){
cout<<ans;
return 0;
}
for(int i=0;i<n;i++){
if(m<=0)break;
if(str[i]=='1')continue;
str[i]='1';
ans+=num[flag];
flag=!flag;
m--;
}
cout<<ans;
}