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 <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
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&&cnt<ans)
{
ansk=K;
ans=cnt;
}
}
printf("%d %d\n", ansk, ans);
}
return 0;
}