leetcode 995. K 連續位的最小翻轉次數 (區間更新,單點查詢)

在僅包含 0 和 1 的數組 A 中,一次 K 位翻轉包括選擇一個長度爲 K 的(連續)子數組,同時將子數組中的每個 0 更改爲 1,而每個 1 更改爲 0。

返回所需的 K 位翻轉的次數,以便數組沒有值爲 0 的元素。如果不可能,返回 -1。

示例 1:

輸入:A = [0,1,0], K = 1
輸出:2
解釋:先翻轉 A[0],然後翻轉 A[2]。
示例 2:

輸入:A = [1,1,0], K = 2
輸出:-1
解釋:無論我們怎樣翻轉大小爲 2 的子數組,我們都不能使數組變爲 [1,1,1]。
示例 3:

輸入:A = [0,0,0,1,0,1,1,0], K = 3
輸出:3
解釋:
翻轉 A[0],A[1],A[2]: A變成 [1,1,1,1,0,1,1,0]
翻轉 A[4],A[5],A[6]: A變成 [1,1,1,1,1,0,0,0]
翻轉 A[5],A[6],A[7]: A變成 [1,1,1,1,1,1,1,1]

提示:

1 <= A.length <= 30000
1 <= K <= A.length

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/minimum-number-of-k-consecutive-bit-flips
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

思路:對於這種翻轉某個區間的問題,很容易想到是貪心,因爲在邊界的點被反轉多次是無意義的,所以我們考慮從左往右掃描,(從右往左也是可行的),對於每個點,我們考慮他當前的狀態(可能是被翻轉過多次的),如果狀態是1,那麼就continue,否則我們就要從這個點開始翻轉,當前i+K > N(即剩餘的長度小於K)則return -1(無法完成翻轉).
如果可以就翻轉這個區間。如果是直接暴力翻轉 時間複雜度就是O(N^2)了,鐵定超時,不過其他語言似乎能通過,應該是給的時間太多了,不過直接暴力不是一種好的做法。

現在問題被轉化爲區間翻轉,單點查詢的一個問題了。

第一種做法,利用樹狀數組

  • 時間複雜度 O(NlogN)
  • 空間複雜度 O(N)

代碼如下:

class Solution {
public:
    int dat[30010];
    int N;
    void add(int l,int v){
        while(l <= N){
            dat[l] += v;
            l += (l&-l);
        }
    }
    int query(int p){
        int s = 0;
        while(p > 0){
            s += dat[p];
            p -= (p&-p);
        }
        return s;
    }
    int minKBitFlips(vector<int>& A, int K) {
        
        N = A.size();
        memset(dat,0,sizeof(dat));
        int res = 0;
        for(int i=0;i<N;++i){
            int value = query(i+1);
            if(A[i] == 0){
                if(value % 2 == 1)  continue;
                else{
                    if(i+K > N) return -1;
            //        printf("i = %d,l = %d,r = %d\n",i,i+1,i+1+K);
                    add(i+1,1);
                    add(i+1+K,-1);
                    res++;
                }
            }
            else{
                if(value % 2 == 0)  continue;
                else{
                    if(i+K > N) return -1;
           //         printf("i = %d,l = %d,r = %d\n",i,i+1,i+1+K);
                    add(i+1,1);
                    add(i+1+K,-1);
                    res++;
                }
            }
        }
        return res;
    }
};

如果不理解樹狀數組這種數據結構可以看這裏。
https://blog.csdn.net/zhao5502169/article/details/75666740

第二種做法,利用標記維護區間

– 時間複雜度O(N)
– 空間複雜度O(N)
這一種做法更好,我第一時間也沒想到。看了下官方題解,纔想起來,自己之前也做過類似的這種題目。因爲查詢總是在更新後面的,所以我們可以用一個變量記錄到當前點發生了多少次翻轉。每次翻轉區間在左邊打一個正的標記,右邊打一個負的標記,即可。
代碼如下:

class Solution {
public:
    int minKBitFlips(vector<int>& A, int K) {
        int N = A.size();
        vector<int> f(N+10,0);//記得要初始化
        int now = 0;//用於記錄當前點被翻轉多少次
        int res = 0;
        for(int i=0;i<N;++i){
            now += f[i];//加上當前點的標記值。
            if((A[i]+now)%2 == 1)
                continue;
            else{
                if(i+K > N) return -1;
                else{
                    res++;
                    now++;//因爲當前點就是左邊界,所以直接++,就相當於是左標記被讀取
                    f[i+K] = -1;
                }
            }
        }
        return res;
    }
};

https://blog.csdn.net/zhao5502169/article/details/81213508
這裏面是我之前寫的,和這個題相似的題目,做法是相同的,利用第二種做法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章