POJ 3276 (开关问题)

题目链接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[5005],now[5005],n,ansm,ansk;
int main()
{
    //freopen("in.txt","r",stdin);
    char key,last='F';
    ansm=0x3f3f3f3f;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf(" %c",&key);
        if(key!=last) f[i]=1;
        last=key;
    }
    for(int k=1;k<=n;k++)
    {
        memcpy(now,f,sizeof(f));
        int cnt=0;
        for(int i=1;i<=n-k+1;i++)
            if(now[i]) {cnt++;now[i+k]^=1;}
        for(int i=n-k+2;i<=n;i++)
            if(now[i]) {cnt=0x3f3f3f3f;break;}
        if(cnt<ansm) {ansm=cnt;ansk=k;}
    }
    printf("%d %d\n",ansk,ansm);
}

 

13594393 neopenx 3276 Accepted 196K 329MS C++ 648B 2014-11-03 17:24:07

 

 

 

posted @ 2014-11-03 17:47  Physcal  阅读(1145)  评论(1编辑  收藏  举报