• 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
    ",ansk,ansm);
    }

     

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

     

     

  • 相关阅读:
    ACM2023
    Archlinux系统运维
    Apache2配置腾讯云SSL证书
    奇异值分解SVD
    剑指offer-不用加减乘除做加法
    负载均衡与缓存
    leetcode简单题6
    python 函数
    Mac-常用命令与快捷键
    GOM通区插件-支持GOM绝对路径-读取配置项-分割字符等功能。不定期更新
  • 原文地址:https://www.cnblogs.com/neopenx/p/4071801.html
Copyright © 2020-2023  润新知