地上有一个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
题源链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof
方法一:广度优先搜索
思路和算法
我们将行坐标和列坐标数位之和大于 k 的格子看作障碍物,那么这道题就是一道很传统的搜索题目,我们可以使用广度优先搜索或者深度优先搜索来解决它,本文选择使用广度优先搜索的方法来讲解。
那么如何计算一个数的数位之和呢?我们只需要对数 x 每次对 10 取余,就能知道数 x 的个位数是多少,然后再将 x 除 10,这个操作等价于将 x 的十进制数向右移一位,删除个位数(类似于二进制中的 >> 右移运算符),不断重复直到 x 为 0 时结束。
同时这道题还有一个隐藏的优化:我们在搜索的过程中搜索方向可以缩减为向右和向下,而不必再向上和向左进行搜索。如下图,我们展示了 16 * 16 的地图随着限制条件 k 的放大,可行方格的变化趋势,每个格子里的值为行坐标和列坐标的数位之和,蓝色方格代表非障碍方格,即其值小于等于当前的限制条件 k。我们可以发现随着限制条件 k 的增大,(0, 0) 所在的蓝色方格区域内新加入的非障碍方格都可以由上方或左方的格子移动一步得到。而其他不连通的蓝色方格区域会随着 k 的增大而连通,且连通的时候也是由上方或左方的格子移动一步得到,因此我们可以将我们的搜索方向缩减为向右或向下。
class Solution {
public:
int movingCount(int m, int n, int k) {
printf("%d\n", k);
if (!k) return 1;
queue<pair<int, int>> Q;
// 向右和向下的方向数组
int dx[2] = {0, 1}, dy[2] = {1, 0};
vector<vector<int>> vis(m, vector<int>(n, 0));
//插入首元素
Q.push(make_pair(0, 0));
//记录已经扫描的点
vis[0][0] = 1;
int ans = 1;
while (!Q.empty()){
auto [x, y] = Q.front();
Q.pop();
for (int i=0; i<2; i++){
int tx = dx[i] + x;
int ty = dy[i] + y;
printf("%d, %d\n", tx, ty);
if (tx<0 || tx>=m || ty<0 || ty>=n ||
vis[tx][ty] || get(tx)+get(ty)>k){
//满足条件不在进行下面工作
continue;
}
Q.push(make_pair(tx, ty));
vis[tx][ty] = 1;
ans++;
}
}
return ans;
}
//计算x的位数之和
int get(int x){
int res = 0;
for(; x; x/=10){
res += x%10;
}
return res;
}
};
方法二:递推
思路
考虑到方法一提到搜索的方向只需要朝下或朝右,我们可以得出一种递推的求解方法。
算法
class Solution {
public:
int movingCount(int m, int n, int k) {
if (!k) return 1;
vector<vector<int>> vis(m, vector<int>(n, 0));
int ans = 1;
//记录已经扫描的点
vis[0][0] = 1;
for (int i=0; i<m; i++){
for(int j=0; j<n; j++){
if((i==0 && j==0) || get(i)+get(j)>k) continue;
//边界判断, 转态转移方程
if(j-1>=0) vis[i][j] |= vis[i][j-1];
if(i-1>=0) vis[i][j] |= vis[i-1][j];
ans += vis[i][j];
}
}
return ans;
}
//计算x的位数之和
int get(int x){
int res = 0;
for(; x; x/=10){
res += x%10;
}
return res;
}
};