【POJ】POJ 3276 Face The Right Way (开关问题)

题目大意

N头牛站成线,有朝前的(F)有朝后的(B),然后每次可以选择大小为k的区间里的牛全部转向,会有一个最小操作M次使得它们全部面朝前方。问:求最小操作M,再此基础上求K。

思路

从前往后进行反转,每次反转K头牛,下一次反转从第一头需要反转的牛开始。
状态搜索的话有2^N 种情况,会超时。

可以遍历一下区间长度K=1到N,找出里面最少次数的。遍历的时候保证一个区间最多反转一次。

先固定一个K,最坏情况下需要N-K+1次反转,每次反转需要操作K头牛,那么时间复杂度是O(N^3 ),需要进行优化。

优化的方法是减少对牛的反转操作。这样就需要知道每头牛与前面反转操作的依赖关系。

对第i头牛来说,第i-K+1,i-K+2,…,i-1头牛的反转都会对第i头牛的方向产生影响。 而且偶数次的反转相当于没有反转,方向还是不变,奇数次的反转才会产生真正的影响。
当然,第0,1,…K-1头牛的方向只受少于前K-1个区间的影响。

怎么来记录这种影响呢?
定义f[i]=1表示区间[i,i+K]进行了反转,否则为0。
而且,这样我们可以通过对f求和来计算第i头牛是否方向变化了。

也就是说,f[i-K+1]+f[i-K+2]+….+f[i-1]的结果为偶数,那么第i头牛方向不变,如果以前是B,就需要反转,如果是F,就不反转。

而这个和的计算,可以进行迭代,从而每次都可以在常数时间内完成。 如下:
记 sum[i] = f[i-K+2]+f[i-K+2]+….+f[i]

sum[i] = 【f[i-K+2]+f[i-K+2]+….+f[i]】
= 【f[i-K+1]+f[i-K+2]+….+f[i-1]】 + f[i] - f[i+K-1]
= sum[i-1] + f[i] - f[i+K-1]

所以,遍历每头牛的时候,顺便更新一下sum就可以了。
最后要注意检查一下剩下的第N-K+1到第N-1头牛是否需要反转。

优化后的时间复杂度为O(N^2 )。

这里写图片描述

这里写图片描述

解答

这里只给出固定K的时候,计算反转次数的Java代码,最终计算的时候最少次数只需要比较一下就可以了。

代码中dir是方向数组,朝后B=1,朝前F=0

    static int calc(int K,int[] dir){
        int N = dir.length;
        int[] f = new int[N]; 
        int res = 0;
        int sum = 0;

        for(int i=0;i+K <= N;i++){
            if((dir[i]+sum) % 2 != 0){
                res++;
                f[i] = 1;
            }
            sum += f[i];
            if(i-K+1>=0){
                sum -= f[i-K+1];
            }
        }

        for(int i=N-K+1;i < N;i++){
            if((dir[i]+sum) % 2 != 0){
                return -1;
            }
            if(i-K+1>0){
                sum -= f[i-K+1];
            }
        }

        return res;
    }
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页