题解 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);
}