USACO35 翻转奶牛(尺取法)

通过这道题了解了不少有关翻转的知识呢......

首先,我们枚举翻转的区间长度k,假设i有个按钮,按下就可以让i~i+k-1翻转,那就有两个状态,按i或不按i(因为按两次相当于没按),那就往后扫一遍,假如要翻转就翻转,不用就不翻,但是这样就会有个问题,每次翻转都要把后面的k个置反,能不能优化?

可以,这就是尺取法。我们用sum表示当前区间翻转了多少次,假如第一头奶牛被翻转,而第二头奶牛也需要(1表示需要,0表示不须),而当前k=2,那就sum=1,我们用(sum+1)%2,假如等于0,就不用翻第二头奶牛了。那尺取体现在那呢,就是我们枚举的区间,前面的区间,假如不能对我当前的点造成影响了,就是i-k的位置,翻不翻转,都对当前位置没有影响,他的影响范围只有i-k~i-1,所以这时sum减去他的影响。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n;
int f[5100],c[5100];
int solve(int k)
{
    int cnt=0,sum=0;
    memset(f,0,sizeof(f));
    for(int i=1;i<=n-k+1;i++)
    {
        if((c[i]+sum)%2==1)
        {
            cnt++;
            f[i]=1;
        }
        sum+=f[i];
        if(i-k+1>=1)sum-=f[i-k+1];
    }
    for(int i=n-k+2;i<=n;i++)
    {
        if((c[i]+sum)%2==1)return 2147483647;
        if(i-k+1>=1)sum-=f[i-k+1];
    }
    return cnt;
}
char ss[10];
int main()
{
    scanf("%d",&n);
    int ansk=1,ansm=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ss+1);
        if(ss[1]=='F') c[i]=0;
        if(ss[1]=='B'){c[i]=1;ansm++;}
    }
    for(int k=2;k<=n;k++)
    {
        int m=solve(k);
        if(m<ansm)ansk=k, ansm=m;
    }
    printf("%d %d
",ansk,ansm);
    return 0;
}
原文地址:https://www.cnblogs.com/AKCqhzdy/p/7725417.html