[POJ 3276] Face The Right Way (翻转问题+技巧)

POJ - 3276
有 N个奶牛排成一列,有些朝前有些朝后,
一次可以翻转连续的 K头奶牛的朝向,K是固定的
求最少翻转次数,以及最小的 K

这种翻转的问题有两个很明显的性质
1) 翻转的位置先后是无关的
2) 同一个地方最多翻转一次

于是枚举 K,再枚举 N依次去翻转
K长度的窗口依次向右滑,如果此时最左边的牛朝后
则一定要翻转,因为此后的操作都与其无关了

不过直接搞的话时间复杂度是 O(n^3)
用到一点小技巧,就是用 rev标记此时是否要翻转
如果翻转,则 rev^=1,并用 note数组标记此次翻转退出的地点
这样时间就优化到了 O(n^2)

有个坑点:
由于我 i从 K开始,一直到 N
所以如果走到 N的时候,还需额外的一个循环判断 N-K+2..N 的是否能翻转完
不仅如此,剩下的翻转退出标记也要一一算到

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Pow2(a) (a*a)

const int maxn=5e3+10;
int N;
bool inpt[maxn];
bool note[maxn];

int main()
{
    while(~scanf("%d", &N))
    {
        CLR(inpt);
        for(int i=1; i<=N; i++)
        {
            char chr;
            scanf(" %c", &chr);
            if(chr=='B') inpt[i]=1;
        }
        int ansk=1,ans=N+10;
        for(int K=1; K<=N; K++)
        {
            int cnt=0;
            bool rev=0;
            CLR(note);
            for(int i=K; i<=N; i++)
            {
                bool now=inpt[i-K+1]^rev;
                if(now)
                {
                    note[i]=1;
                    cnt++;
                    rev^=1;
                }
                rev^=note[i-K+1];
            }
            bool ok=1;
            for(int i=N-K+2; i<=N; i++)
            {
                if(inpt[i]^rev){ok=0;break;}
                rev^=note[i];
            }
            if(ok&&cntprintf("%d %d\n", ansk, ans);
    }
    return 0;
}

你可能感兴趣的:(脑洞,技巧,poj)