K 连续位的最小翻转次数
在仅包含 0 和 1 的数组 A 中,一次 K 位翻转包括选择一个长度为 K 的(连续)子数组,同时将子数组中的每个 0 更改为 1,而每个 1 更改为 0。
返回所需的 K 位翻转的最小次数,以便数组没有值为 0 的元素。如果不可能,返回 -1。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-number-of-k-consecutive-bit-flips
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
贪心算法
// 贪心算法, 遇到0就翻转
fn min_k_bit_flips(a: Vec<i32>, k: i32) -> i32 {
let mut cnt = 0;
let mut left = 0;
let mut a_clone = a.clone();
let k:usize = k as usize;
while left < a_clone.len() + 1 - k as usize {
if a_clone[left] == 0 {
// 翻转
for idx in 0..k {
a_clone[idx + left] = 1 - a_clone[idx + left];
}
cnt += 1;
print!("left:{}\t cnt:{}\t, {:?}\n", left, cnt, a_clone);
}
left += 1;
}
for idx in 0..k {
let idx: usize = a_clone.len() - 1 - idx as usize;
if a_clone[idx] == 0 {
return -1;
}
}
return cnt;
}
fn main() {
let nums: Vec<i32> = vec![1,1,0];
let k:i32 = 2;
print!("output is {}\n", min_k_bit_flips(nums, k));
}
优化
分析得知,没有必要每次都做翻转的操作
思路
- 一个数组k,记录对应位置的翻转次数
- 第 i 位若要翻转, 这 区间[i, i+k]内的次数 都加1, 翻转总次数 + 1
需要考虑的几个问题
- 如何判断第 i 位是否要翻转???
由于题目的特殊性: 这是一个只有0, 1 的数组 和 一个翻转的操作
不难得知:即奇数次反转之后数字发生了变换, 偶数次转换还是该值
所以a[i + 1] == 0 && k[i] % 2 == 0
或者a[i + 1] == 1 && k[i] % 2 == 1
时需要翻转
综上(a[i + 1] + k[i]) % 2 == 0
时认为需要翻转
注意到 有一个对区间所有数都加一的操作, 这样可以考虑差分数组的思路
即:
- 定义一个拆分数组
- 第 i 位若要翻转, 则 a[i] + 1, a[i + k] - 1, 这样对于第i位的翻转次数 就可以表示为∑ a[i] 可以用一个变量来记录
第一次优化后的代码:
fn min_k_bit_flips(a: Vec<i32>, k: i32) -> i32 {
let mut left = 0;
let mut cnt = 0;
let mut diff_sum = 0;
let mut diff = vec![0; a.len() + 1];
let k:usize = k as usize;
while left < a.len() {
// 计算前n的和, 也就是第n位翻转的次数
diff_sum += diff[left];
if (a[left] + diff_sum) % 2 == 0 {
// 需要翻转
if left + k > a.len() {
// 无法进行翻转
return -1;
}
cnt += 1;
diff[left] += 1; diff_sum += 1;
diff[left + k] -= 1;
}
left += 1;
}
return cnt;
}
fn main() {
let nums: Vec<i32> = vec![0,0,0,1,0,1,1,0];
let k:i32 = 3;
print!("output is {}\n", min_k_bit_flips(nums, k));
}