描述
地上有一个rows行和cols列的方格。坐标从 [0,0] 到 [rows-1,cols-1]。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于threshold的格子。 例如,当threshold为18时,机器人能够进入方格[35,37],因为3+5+3+7 = 18。但是,它不能进入方格[35,38],因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
范围:
1 <= rows, cols<= 100;0 <= threshold <= 20
示例
input1:1,2,3
output1:3
input2:0,1,3
output2:1
input3:10,1,100
output3:29
说明3:
[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7],[0,8],[0,9],[0,10],[0,11],[0,12],[0,13],[0,14],
[0,15],[0,16],[0,17],[0,18],[0,19],[0,20],[0,21],[0,22],[0,23],[0,24],[0,25],[0,26],[0,27],
[0,28] 这29种,后面的[0,29],[0,30]以及[0,31]等等是无法到达的
input4:5,10,10
output4:21
思路
从题目中可以看出,由于机器人每次只能移动1格,并且行列坐标的数位之和有限制,因此必然需要对能够访问的格子进行遍历,很容易想到两种方法:深度优先和广度优先。
时间复杂度为:O(M*N)
,考虑极端情况每个格子都访问一遍
空间复杂度为:O(M*N)
,需要创建二维数组
1、深度优先(DFS)
DFS算法的思路是往一个方向一直走到底,不满足条件时才会回头,继续走到底,等所有能够访问的格子都访问了遍历就结束了。在题目中,机器人可以在四个方向上移动,因此DFS算法的主体是往四个方向走,并且当前的格子需要标记为已访问,出口条件则是分为几种:
(1)行、列的索引超出范围;行列坐标的数位之和超出阈值
(2)当前格子已访问
——粗暴版
在该版本中,直接把计数的变量设为全局,并且按照四个方向进行移动。这种思路法简单粗暴,对DFS的问题具有通用性
public class Solution {
public int count = 0;//全局变量计数
public int movingCount(int threshold, int rows, int cols) {
boolean[][] arr = new boolean[rows][cols];
dfs(arr, 0, 0, threshold);
return count;
}
//计算数字的数位之和
public int getBitCnt(int num){
int cnt = 0;
while(num != 0){
cnt += num % 10;
num /= 10;
}
return cnt;
}
//DFS算法的实现
public void dfs(boolean[][] arr, int row, int col, int th){
boolean flag = row < 0 || row == arr.length;
flag = flag || col < 0 || col == arr[0].length;
flag = flag || getBitCnt(row) + getBitCnt(col) > th;
flag = flag || arr[row][col] == true;
if(flag) return;
arr[row][col] = true;
count++;
dfs(arr, row+1, col, th);
dfs(arr, row-1, col, th);
dfs(arr, row, col+1, th);
dfs(arr, row, col-1, th);
}
}
——简化版
通过题目进行分析,由于起点在左上角(0, 0),因此下一格必然是右或者下,因此可以不考虑左和上;同时,为了节省空间,可以直接让DFS返回计数值,由3部分组成:
(1)当前已经满足条件的格子,计数为1
(2)当前格子右方的格子:dfs(arr, row, col+1, th)
(3)当前格子下方的格子:dfs(arr, row+1, col, th)
public int dfs(boolean[][] arr, int row, int col, int th){
boolean flag = row == arr.length || col == arr[0].length;
flag = flag || getBitCnt(row) + getBitCnt(col) > th;
flag = flag || arr[row][col] == true;
if(flag) return 0;
arr[row][col] = true;
return 1 + dfs(arr, row+1, col, th) + dfs(arr, row, col+1, th);
}
2、广度优先(BFS)
BFS算法的思路是按照距离进行访问,在本题中先访问离自己最近的一圈,接着往外扩张,直到整个格子访问完成。在该算法中,第一圈就是左上角(0, 0),第二圈则是右和下,第三圈则是第二圈所有元素的右和下,依此类推。
在BFS算法中,一般实现的方法是使用队列,这样可以保证最近的一圈访问后,这一圈的元素被标记为以访问并且下一圈的所有元素也被加入到队列中
import java.util.Queue;
import java.util.LinkedList;
public class Solution {
public int movingCount(int threshold, int rows, int cols) {
boolean[][] flag = new boolean[rows][cols];//用于标记格子是否被访问
int cnt = 0;//用以计数
Queue<int[]> que = new LinkedList<>();//队列只存放某一圈的元素
que.add(new int[]{0, 0});//第一圈从(0, 0)开始,只有一个元素
while(!que.isEmpty()){
int[] e = que.poll();
int row = e[0], col = e[1];
if(row == rows || col == cols || getBitCnt(row)+getBitCnt(col) > threshold || flag[row][col] == true)
continue;
cnt++;
flag[row][col] = true;
//第一圈弹出后,则加入第二圈:即其右和下方的元素
que.add(new int[]{row+1, col});
que.add(new int[]{row, col+1});
}
return cnt;
}
public int getBitCnt(int num){
int cnt = 0;
while(num != 0){
cnt += num % 10;
num /= 10;
}
return cnt;
}
}