在僅包含 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
這裏面是我之前寫的,和這個題相似的題目,做法是相同的,利用第二種做法。