xxleyi / loop_invariants

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

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

leetcode 面试题13. 机器人的运动范围

xxleyi opened this issue · comments

题:

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

 

示例 1:

输入:m = 2, n = 3, k = 1
输出:3
示例 1:

输入:m = 3, n = 1, k = 0
输出:1
提示:

1 <= n,m <= 100
0 <= k <= 20

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


解:

如果有过「广度搜索」经验,这题算比较直接。想要正确解题,需要注意的是处理好细节,保证 bfs 过程中,正确维护队列。

var movingCount = function(m, n, k) {
  function carrySum (pair) {
    let sum = 0
    for (let e of pair) if (e !== ",") sum += +e
    return sum
  }

  function toIJ(e) {
    return e.split(',').map(e => +e)
  }

  function validIJ(i, j) {
    return i >= 0 && i < m && j >= 0 && j < n
  }

  // helper varaiable
  let dir = [[-1, 0], [1, 0], [0, -1], [0, 1]]

  
  // initialize loop invariants related variables: head and tail pointer, queue and doneSet
  let head = tail = 0
  let queue = Array(m * n), doneSet = new Set()
  queue[head] = "0,0"
  doneSet.add(queue[head])

  // bfs by the queue
  while (queue[head] != null) {
    // update head pointer to maintain loop invariant
    let e = queue[head++]
    let [i, j] = toIJ(e)
    // update tail pointer to maintain loop invariant
    for (let [di, dj] of dir) {
      let pair = [di + i, dj + j].join(",")
      if (!doneSet.has(pair) && validIJ(i + di, j + dj) && carrySum(pair) <= k) {
        queue[++tail] = pair
        doneSet.add(queue[tail])
      }
    }
  }

  // loop termination: head pointer value is our answer, as well as tail or doneSet.size()
  console.assert(head == tail && tail == doneSet.size())
  return head
};