zoukankan      html  css  js  c++  java
  • leetcode 995. K 连续位的最小翻转次数(差分)

    在仅包含 0 和 1 的数组 A 中,一次 K 位翻转包括选择一个长度为 K 的(连续)子数组,同时将子数组中的每个 0 更改为 1,而每个 1 更改为 0。

    返回所需的 K 位翻转的最小次数,以便数组没有值为 0 的元素。如果不可能,返回 -1。

    假hard真medium。这种题一定是从一边开始处理,逐步翻转。因为如果有解的话,翻转次数是一定的。所有的翻转区间一定是由一个或者多个有重叠的翻转区间(成为联通区间)组成。比如[1, 4]、[3, 6]和[5, 8]构成了一个联通区间[1, 8]。这个问题实际上可以拆分为处理这些联通区间。可以知道如果有解的话,联通区间的左右端点一定只被翻转了一次(翻转奇数次相当于翻转一次,翻转偶数次相当于没有翻转)。那么可以从左端点出发,对于每个点,如果为1则跳过,为0则翻转以当前位置为左端点长度为k的区间。如果能处理完该联通区间的话则有解。

    对于这个题,有以上结论的话实际上直接遍历1到i + k - 1,当前为1跳过,为0则翻转。朴素地翻转显然会t,这里可以用差分的思想对区间进行翻转,在遍历的时候求前缀和得到当前点的翻转次数,偶数次忽略,奇数次则用a[i] = a[i] ^ 1来更新a[i],再进行判断。注意由于遍历范围并非1到n,因此最后一部分的a[i]需要单独更新。

    class Solution {
    public:
        int n, k, a[30005], diff[30005];
        void add(int x, int y)
        {
        	diff[x]++;
    	    diff[y + 1]--;
        }
        int minKBitFlips(vector<int>& A, int K) {
            k = K, n = A.size();
            for(int i = 0; i < A.size(); i++) a[i + 1] = A[i];
            int ans = 0;
    	    for(int i = 1; i + k - 1 <= n; i++)
    	    {
    		    diff[i] += diff[i - 1];
    		    if(diff[i] & 1) a[i] ^= 1;
    		    if(a[i] == 1) continue;
    		    else
    		    {
    		    	add(i, i + k - 1);
    		    	ans++;
    		    	a[i] = 1;
    		    }
    	    }
    	    for(int i = n - k + 2; i <= n; i++)
    	    {
    	    	diff[i] += diff[i - 1];
    	    	if(diff[i] & 1) a[i] ^= 1;
    	    }
            for(int i = 1; i <= n; i++) if(a[i] == 0) return -1;
            return ans;
        }
    };
    
  • 相关阅读:
    原型prototype
    this
    作用域、闭包、模块
    嵌入式面试资料
    一些嵌入式面试题目的集锦
    优先级反转
    struct和union的区别
    (转)typedef和#define的用法与区别
    const 和 #define区别
    白话经典算法系列之 快速排序 快速搞定
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/14413188.html
Copyright © 2011-2022 走看看