xxleyi / loop_invariants

记录以及整理「循环不变式」视角下的算法题解

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

leetcode 995. K 连续位的最小翻转次数

xxleyi opened this issue · comments

题:

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

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

995. K 连续位的最小翻转次数 - 力扣(LeetCode)


解:

目标是将 0 翻转为 1,翻转 0 的时候,有可能把 1 翻转为 0,如果被多翻转了两次,1 又变回 1

所以我们有一个「循环不变量」相关的变量 flip,类型是布尔值。针对第 i 个元素,如果 flipfalse,遇 0 翻转;反之,遇 1 翻转。

由于一次翻转涉及 k 个元素,所以隐约有个担忧: flip 的状态无法仅仅由 flip = !flip 这一个形式决定。我们仔细考察翻转能影响的范围 [i, i + k),在翻转 i 处元素时,i + k 处的元素不受当前 flip 状态的影响。所以当我们到了 i + k 处时,flip 需要额外进行一次翻转,以抵消掉 i 处的翻转。

所以,我们需要第二个「循环不变量」相关的变量 flipOneMore,类型是数组,每一个元素初始值为 flase[0, k) 这个范围没有太大用处,且长度应该为 A.length + 1

const minKBitFlips = (A, K) => {
  // initialize two loop invariants related variables: flip boolean and flipOneMore array
  let flip = false, flipOneMore = Array(A.length + 1).fill(false)
  // initialize count to save answer
  let count = 0

  for (let i = 0; i < A.length; i++) {
    // update flip to ensure one loop invariant before loop
    if (flipOneMore[i]) flip = !flip
    if (+flip == A[i]) {
      // check if cross border
      if (i + K > A.length) return -1
      // update count
      // update flip and flipOneMore to ensure two loop invariants after loop
      count += 1
      flip = !flip
      flipOneMore[i + K] = true
    }
  }
  
  // if normal termination: count is our answer for A and K
  return count
}