USACO07 MAR Face The Right Way G

疫情当下,美帝又开始倒牛奶了,这一幕似曾相识啊~~~

这个题目非常的应景,又是美国佬的奶牛

【题目地址】

【一句话题意】

N头牛排成一列1<=N<=5000。每头牛或者向前或者向后。
为了让所有牛都 面向前方,农夫每次可以将K头连续的牛转向1<=K<=N,
求操作的最少次数M和对应的最小K。

【Input】

第1行:单个整数:N
第2..N + 1行:第i + 1行包含单个字符F或B,指示母牛i面向前还是面向后。

【Output】

第1行:两个以空格分隔的整数:K和M

【Sample】

input
7
B
B
F
B
F
B
B
output
3 3

【分析&思路】

区间修改?
线段树、树状数组,暴力枚举01修改——boom
我们注意到每一个点只有两种状态0|1
那么如果修改两次的话就相当于没改咯
我们把区间分开考虑
怎么说
就是把最左边连续的一段进行操作变为全部向前
于是在后续的操作中我们duck不必再考虑前面的
这思路没啥问题,直接上暴力

#include <cstdio>
using namespace std;
int n, a[5005][5005], minn = 99999, step , pin;

int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; i++){
        char ch;
        getchar();
        ch = getchar();
        if(ch == 'B') a[0][i] = 0;
        else a[0][i] = 1;
    }
    for(int i = 1; i <= n; i++){
        step = 0;
        int flag = 0;
        for(int j = 1; j <= n; j++)
        a[i][j] = a[0][j];
        for(int j = 1; j <= n - i + 1; j++){
            if(!a[i][j]){
            for(int k = 0; k < i ; k++){
                a[i][j + k] ^= 1;
            }
            step++;
            if(step > minn) break;
            }
        }
        for(int j = n - i + 2; j <= n; j++){
            if(!a[i][j]){
                flag = 1;
                break;
            }
        }
        for(int j = 1; j <= n; j++)
        if(!flag) {
            if(minn > step)
            {
                minn = step;
                pin = i;
            }
        }
    }
    printf("%d %d", pin, minn);
    return 0;
} 

然后……T掉了……
那么我们再来考虑优化的方法
类似于差分
在此,可能叫做“异或分”
记录区间长度为k
每次反转的时候更改标记为0或1
给两个区间分别异或
则不改变前后区间的相对状态
总的时间复杂度为(O(n^2))
emmmmm就这样叭
代码非原创

#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
",anslen, mincnt);
} 
风吹过,我来过~
原文地址:https://www.cnblogs.com/rui-4825/p/12642272.html