开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 17 天,点击查看活动详情
题目
面试题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。请问该机器人能够到达多少个格子?
示例:
输入:m = 2, n = 3, k = 1
输出:3
分析一
按照 24 号矩阵题的经验解了出来,先记下自己的思路
先把题目要求逐个拆解
- 遍历矩阵,判断每个格子坐标是否满足要求
for (let i = 0; i < m; i ++) {
for (let j = 0; j < n; j ++) {
if (check(i, j)) {
// ...
}
}
}
- 完善判断方法,判断传入坐标是否满足 坐标每个位上数字之和是否小于 k
由于求解坐标之和只能个位相加,单独写个方法来计算坐标的值
function getTotal (i , j) {
function handle(num) {
let str = num.toString()
let result = 0
for (i of str) {
result += i%10
}
return result
}
return handle(i) + handle(j)
}
判断方法雏形
function check (i, j) {
// 判断和是否大于 k
if (getTotal(i, j) > k) {
return false
} else {
return true
}
}
加入边界条件
if ( i == 0 && j == 0) {
return true
}
if (i >= m || j >= n || i < 0 || j < 0) {
return false
}
- 这时就剩关键点:判断该点是否可以到达了
我们可以用回溯的方法进行判断
即如果该点可以到达,那么它必须要满足它上边或者左边的点可以到达
为了减少计算量,我们使用一个新的矩阵来判断每个点是否可以到达
let canVisit = new Array(m)
for (let i = 0 ; i < m; i ++) {
canVisit[i] = new Array(n).fill(false)
}
完善判断逻辑,如果该点满足要求并且可以走到,将该点标为 true,并将最终结果返回
// 判断是否可以到达该点
let top = [i - 1, j]
let left = [i, j - 1]
result = check(top[0], top[1]) || check(left[0], left[1])
if (result) {
canVisit[i][j] = true
return result
} else {
return result
}
最后用一个计数变量记下所有满足条件的点,既是题目所求
实现
function movingCount(m: number, n: number, k: number): number {
let index = 0
let canVisit = new Array(m)
for (let i = 0 ; i < m; i ++) {
canVisit[i] = new Array(n).fill(false)
}
function getTotal (i , j) {
function handle(num) {
let str = num.toString()
let result = 0
for (i of str) {
result += i%10
}
return result
}
return handle(i) + handle(j)
}
function check (i, j) {
// 边界
if ( i == 0 && j == 0) {
return true
}
if (i >= m || j >= n || i < 0 || j < 0) {
return false
}
// 访问判定
if (canVisit[i][j]) {
return true
}
let result = false
// 判断和是否大于 k
if (getTotal(i, j) > k) {
return false
}
// 判断是否可以到达该点
let top = [i - 1, j]
let left = [i, j - 1]
result = check(top[0], top[1]) || check(left[0], left[1])
if (result) {
canVisit[i][j] = true
return result
} else {
return result
}
}
let tmp = []
for (let i = 0; i < m; i ++) {
for (let j = 0; j < n; j ++) {
if (check(i, j)) {
tmp.push([i, j])
index ++
}
}
}
console.log(tmp)
return index
};
分析二
看了参考答案做一波优化
- 使用数学方法优化一下求和函数
function getTotal (i , j) {
function handle(num) {
let res = 0
while(num) {
res += num%10
num = Math.floor(num/10)
}
return res
}
return handle(i) + handle(j)
}
- 使用深度优先遍历(从右和下方一层层遍历,顺便求和)
function dfs (i, j) {
// 边界和是否访问过
if (i >= m || j >= n || visited[i][j]) {
return 0
}
// 判断是否满足要求
if (getTotal(i, j) <= k) {
visited[i][j] = 1
return 1 + dfs(i+1, j) + dfs(i, j+1)
} else {
return 0
}
}
实现
function movingCount(m: number, n: number, k: number): number {
function getTotal (i , j) {
function handle(num) {
let res = 0
while(num) {
res += num%10
num = Math.floor(num/10)
}
return res
}
return handle(i) + handle(j)
}
let visited = new Array(m)
for (let i = 0; i < m; i ++) {
visited[i] = new Array(n).fill(0)
}
function dfs (i, j) {
// 边界和是否访问过
if (i >= m || j >= n || visited[i][j]) {
return 0
}
// 判断是否满足要求
if (getTotal(i, j) <= k) {
visited[i][j] = 1
return 1 + dfs(i+1, j) + dfs(i, j+1)
} else {
return 0
}
}
return dfs(0, 0)
};