劍指 Offer 13. 機器人的運動範圍

地上有一個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

示例 2:

輸入:m = 3, n = 1, k = 0
輸出:1

提示:

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

一、深度優先遍歷DFS

根據K神思路寫的程式碼:

class Solution {

    public int movingCount(int m, int n, int k) {
boolean[][] visited = new boolean[m][n];
return dfs(visited, m, n, k, 0, 0);
} private int dfs(boolean[][] visited, int m, int n, int k, int i, int j) {
if(i >= m || j >= n || visited[i][j] || bitSum(i) + bitSum(j) > k) return 0;
visited[i][j] = true;
return 1 + dfs(visited, m, n, k, i + 1, j) + dfs(visited, m, n, k, i, j + 1) ;
} private int bitSum(int n) {
int sum = 0;
while(n > 0) {
sum += n % 10;
n /= 10;
}
return sum;
}
}

註釋版本:

class Solution {
// 棋盤的行列
int m, n;
// 記錄位置是否被遍歷過
boolean[][] visited; public int movingCount(int m, int n, int k) {
this.m = m;
this.n = n;
visited = new boolean[m][n];
return dfs(0, 0, k);
} private int dfs(int i, int j, int k) {
// i >= m || j >= n是邊界條件的判斷
if (i >= m || j >= n
// visited[i][j]判斷這個格子是否被訪問過
|| visited[i][j] == true
// k < sum(i, j)判斷當前格子座標是否滿足條件
|| sum(i, j) > k) {
return 0;
}
// 標註這個格子被訪問過
visited[i][j] = true;
// 沿著當前格子的右邊和下邊繼續訪問
return 1 + dfs(i + 1, j, k)
+ dfs(i, j + 1, k);
} // 計算兩個座標數字的和
private int sum(int i, int j) {
int sum = 0;
while (i != 0) {
sum += i % 10;
i /= 10;
}
while (j != 0) {
sum += j % 10;
j /= 10;
}
return sum;
}
}

k神簡潔的程式碼:

class Solution {
int m, n, k;
boolean[][] visited;
public int movingCount(int m, int n, int k) {
this.m = m; this.n = n; this.k = k;
this.visited = new boolean[m][n];
return dfs(0, 0, 0, 0);
}
public int dfs(int i, int j, int si, int sj) {
if(i >= m || j >= n || k < si + sj || visited[i][j]) return 0;
visited[i][j] = true;
return 1 + dfs(i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj) + dfs(i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8);
}
}

二、廣度優先遍歷 BFS

  • BFS/DFS : 兩者目標都是遍歷整個矩陣,不同點在於搜尋順序不同。DFS 是朝一個方向走到底,再回退,以此類推;BFS 則是按照“平推”的方式向前搜尋。
  • BFS 實現: 通常利用佇列實現廣度優先遍歷。

這個程式碼:有點繁冗,但可以讓小白看懂程式碼是如何執行的。

class Solution {
public int movingCount(int m, int n, int k) {
//狀態:dp[i][j]代表第i,j個格子能否走到
boolean[][] dp = new boolean[m][n];
dp[0][0] = isValid(0, 0, k);
//轉移方程
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if(i == 0 && j == 0) continue;
else if(i == 0) dp[i][j] = isValid(i, j, k) && dp[i][j - 1];
else if(j == 0) dp[i][j] = isValid(i, j, k) && dp[i - 1][j];
else dp[i][j] = isValid(i, j, k) && (dp[i - 1][j] || dp[i][j - 1]);
}
}
int count = 0;
for (boolean[] row : dp) {
for (boolean ele : row) {
if (ele) {
count++;
}
}
}
return count;
}
public boolean isValid(int i, int j, int k) {
int sum = 0;
while (i != 0) {
sum += i % 10;
i /= 10;
}
while (j != 0) {
sum += j % 10;
j /= 10;
}
return sum <= k;
}
}

這個程式碼是為了求sum又簡化了程式碼,但跟k神比起確實還差一點。

public int movingCount(int m, int n, int k) {
boolean[][] nums = new boolean[m][n];
nums[0][0] = isValid(0, 0, k);
int sum = 1;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (i == 0 && j == 0) {
continue;
} else if (i == 0) {
nums[i][j] = nums[i][j - 1] && isValid(i, j, k);
} else if (j==0) {
nums[i][j] = nums[i - 1][j] && isValid(i, j, k);
} else {
nums[i][j] = (nums[i - 1][j] || nums[i][j - 1]) && isValid(i, j, k);
}
if (nums[i][j]) sum++;
}
}
return sum;
}
public boolean isValid(int m, int n, int k) {
int sum = 0;
while (m != 0 || n != 0) {
if (m != 0) {
sum += m % 10;
m /= 10;
}
if (n != 0) {
sum += n % 10;
n /= 10;
}
}
return sum <= k;
}
}

k神更簡潔的程式碼:

class Solution {
public int movingCount(int m, int n, int k) {
boolean[][] visited = new boolean[m][n];
int res = 0;
Queue<int[]> queue= new LinkedList<int[]>();
queue.add(new int[] { 0, 0, 0, 0 });
while(queue.size() > 0) {
int[] x = queue.poll();
int i = x[0], j = x[1], si = x[2], sj = x[3];
if(i >= m || j >= n || k < si + sj || visited[i][j]) continue;
visited[i][j] = true;
res ++;
queue.add(new int[] { i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj });
queue.add(new int[] { i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8 });
}
return res;
}
}

參考連結:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/solution/mian-shi-ti-13-ji-qi-ren-de-yun-dong-fan-wei-dfs-b/