题解 P2882 【[USACO07MAR]面对正确的方式Face The Right Way】
贪心,枚举,差分
因为同一个点翻转两次就与没有翻转的效果相同了,因此我们有一个贪心策略为:
从左到右对于出现的每一个B翻转一次从当前点开始的区间,就能保证是最优解。
样例的模拟:
如果当前翻转的区间长度为3
粗体表示当前下标
B B F B F B B
(B B F) B F B B
F F B B F B B
F F B B F B B
F F B B F B B
F F (B B F) B B
F F F F B B B
F F F F B B B
F F F F B B B
F F F F (B B B)
F F F F F F F
F F F F F F F
F F F F F F F
但是我们会发现这样是n^2的,再枚举长度,就变为了n^3.因此,需要对区间翻转差分一下,总时间复杂度就是n^2的了
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 10009;
int n;
bool A[maxn],B[maxn];
int main(){
scanf("%d",&n);
char ch;
for(int i=1;i<=n;++i){
cin>>ch;A[i]=ch=='B'?0:1;
}
int mincnt=0x7fffffff,anslen;
for(int len=1;len<=n;++len){
memset(B,0,sizeof B);
bool b=0,flag=1;int cnt=0;//flag记录当前长度是否有解
for(int i=1;i<=n;++i){//因为区间翻转只有状态0和1,所以用^代替+和-
b^=B[i];//数组B为记录差分的数组
if(!(A[i]^b)){//若当前位置为0
if(i+len-1>n){flag=0;break;}//唯一的失败条件:一次翻转的长度大于剩余的长度
b^=1,B[i+len]^=1;
++cnt;
}
}
if(flag){if(cnt<mincnt)mincnt=cnt,anslen=len;}
}printf("%d %d\n",anslen, mincnt);
}