面试题13-机器人的运动范围(详细解释一下BFS解法与模板)
地上有一个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。请问该机器人能够到达多少个格子?
题目拿到手发现是搜索问题(有多少个格子,需要遍历去搜索的)思路就是使用BFS(广搜)或者DFS(深搜)。这里面主要解释一下BFS。BFS使用队列,如果以树为例就是把树根放到队列中,目前队列就根结点一个,然后根据根结点相连的结点去进行展开(放入队列中)然后根结点访问完就pop掉,根据放入队列的下一个top结点(就是根结点相连的某个结点)继续重复上述过程,把该结点相连的结点放入队列中,然后访问该节点完毕pop出,该队列的top值替换。直到最后队列中无元素,访问完毕。
这里面的题意显然也能用BFS方法,但是题中的点之间的关系怎么和树的结点相连关系去转换呢?仔细读题发现,在结点的上下左右均可移动一格,就相当于中间结点是根结点,其余上下左右的结点就相当于和根结点相连的子结点。 按照这样的访问顺序去将结点去放入到队列中,如此循环,直到队列的元素都pop出去结束。在题目之外还要考虑的问题就是对于这样的矩阵上下左右搜索时候考虑边界条件,同时对于结点还要加上visit的限制,放置访问到已经访问过的结点,把这些细节考虑清楚,那么这种题就不难做了。
BFS模板如下:(负雪明烛的模板)
while queue 不空: cur = queue.pop() for 节点 in cur的所有相邻节点: if 该节点有效且未访问过: queue.push(该节点) 如果要确定当前遍历到了哪一层,BFS模板如下。这里增加了level表示当前遍历到二叉树中的哪一层了,也可以理解为在一个图中,现在已经走了多少步了。size表示在开始遍历新的一层时,队列中有多少个元素,即有多少个点需要向前走一步。 level = 0 while queue 不空: size = queue.size() while (size --) { cur = queue.pop() for 节点 in cur的所有相邻节点: if 该节点有效且未被访问过: queue.push(该节点) } level ++;
1 class Solution { 2 public: 3 int movingCount(int m, int n, int k) { 4 vector<vector<int>> visited(m, vector<int>(n, 0)); 5 queue<pair<int, int>> q; 6 q.push({0, 0}); 7 int res = 0; 8 visited[0][0] = 1; 9 while (!q.empty()) { 10 auto front = q.front(); q.pop(); 11 int x = front.first; 12 int y = front.second; 13 res += 1; 14 for (auto d : directions) { 15 int new_x = x + d.first; 16 int new_y = y + d.second; 17 if (new_x < 0 || new_x >= m || new_y < 0 || new_y >= n 18 || visited[new_x][new_y] == 1 || 19 sumDigit(new_x, new_y) > k) { 20 continue; 21 } 22 q.push({new_x, new_y}); 23 visited[new_x][new_y] = 1; 24 } 25 } 26 return res; 27 } 28 int sumDigit(int i, int j) { 29 int sum = 0; 30 while (i > 0) { 31 sum += i % 10; 32 i /= 10; 33 } 34 while (j > 0) { 35 sum += j % 10; 36 j /= 10; 37 } 38 return sum; 39 } 40 private: 41 vector<pair<int, int>> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; 42 };
——世界上从来不乏优秀的人,我只是想接近他们一点。