题目链接http://poj.org/problem?id=3276

题目大意:有一些牛,头要么朝前要么朝后,现在要求确定一个连续反转牛头的区间K,使得所有牛都朝前,且反转次数m尽可能小。

解题思路

首先不要看错题意了,不是求最小K,不要二分。而且反转区间长度一定是K,小于K是不能反转的。

很明显得枚举K(1...n),并且有以下反转思路:

①从第一头牛开始,如果朝前,不管了。看下一头牛,如果朝后反转K长度区间.....一直扫到区间结束。

②第一趟结束后,如果不符合要求,继续重复①,直到所有牛都朝前。

这样复杂度是O(n^3),5000*5000*5000,标准TLE。

其实确定反转次数只需要扫一趟就行了,没有必要来回多趟。O(n^2)就能解决,这里借鉴了tmeteorj的依赖关系法,非常简洁。

它的思路是这样的:

f[i]保存的当前牛与前一头牛的关系,不同1,同0。其中设置一个0牛,方向为F。

这样,如果f[i]=1,则表示[i-1,i+k-1]这个区间需要反转,其中f值变化的只有f[i]和f[i+k]。中间的值没有变化。

对于每个K,从1扫到n-k+1,如果f[i]=1则进行反转操作,反转之后变化的部分立刻反馈,这样当处理i+1时,就能保证当前状态是处理i+1的最后一趟的状态。

原因很简单,在O(n^3)的方法里,我们来回扫,不过是把值来回重复循环,毫无意义。使用这种关系依赖法之后,就可以避免这些毫无意义的循环。

对于n+k+2~n的部分,只要出现需要反转的,则本次K是无效的。继续看下一个K。

否则,更新一下ansm和ansk。

#include "cstdio"
#include "cstring"
int f[],now[],n,ansm,ansk;
int main()
{
//freopen("in.txt","r",stdin);
char key,last='F';
ansm=0x3f3f3f3f;
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf(" %c",&key);
if(key!=last) f[i]=;
last=key;
}
for(int k=;k<=n;k++)
{
memcpy(now,f,sizeof(f));
int cnt=;
for(int i=;i<=n-k+;i++)
if(now[i]) {cnt++;now[i+k]^=;}
for(int i=n-k+;i<=n;i++)
if(now[i]) {cnt=0x3f3f3f3f;break;}
if(cnt<ansm) {ansm=cnt;ansk=k;}
}
printf("%d %d\n",ansk,ansm);
}
13594393neopenx3276Accepted196K329MSC++648B2014-11-03 17:24:07
04-26 07:51