题目描述
地上有一个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
题解
如何计算一个数的数位之和呢?我们只需要对数 x 每次对 10 取余,就能知道数 x 的个位数是多少,然后再将 x 除 10,这个操作等价于将 x 的十进制数向右移一位,删除个位数(类似于二进制中的 >> 右移运算符),不断重复直到 x 为 0 时结束。
题解1:广度优先搜索:BFS
class Solution {
// 计算 x 的数位之和
int get(int x) {
int res=0;
for (; x; x /= 10) {//x > 0,就需要求余,不断求余求个位、十位、百位...的值。
res += x % 10;
}
return res;
}
public:
int movingCount(int m, int n, int k) {
if (!k) return 1;
queue<pair<int,int> > Q;//https://blog.youkuaiyun.com/Enterprise_/article/details/73695255
// 向右和向下的方向数组
int dx[2] = {0, 1};
int 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;//vis[x][y] == 1表示该位置满足条件,机器人可以跳到
while (!Q.empty()) {
auto [x, y] = Q.front();//队列先进先出,尾进头出
Q.pop();
for (int i = 0; i < 2; ++i) {
int tx = x + dx[i];//当前位置(x,y)像右或者向下移动得到新位置(tx,ty)
int ty = y + dy[i];
if (tx < 0 || tx >= m || ty < 0 || ty >= n || vis[tx][ty] || get(tx) + get(ty) > k) continue;//判断新位置(tx,ty)是否满足条件
Q.push(make_pair(tx, ty));//满足条件将其加入队列
vis[tx][ty] = 1;
ans++;
}
}
return ans;
}
};
复杂度分析:
- 时间复杂度:O(mn),其中 m 为方格的行数,n 为方格的列数。考虑所有格子都能进入,那么搜索的时候一个格子最多会被访问的次数为常数,所以时间复杂度为 O(2mn)=O(mn)。
- 空间复杂度:O(mn),其中 m 为方格的行数,n 为方格的列数。搜索的时候需要一个大小为 O(mn) 的标记结构用来标记每个格子是否被走过。
题解2:递推
思路:
考虑到方法一提到搜索的方向只需要朝下或朝右,我们可以得出一种递推的求解方法。
算法:
定义 vis[i][j] 为 (i, j) 坐标是否可达,如果可达返回 1,否则返回 0。【有动态规划的感觉了】
首先 (i, j) 本身需要可以进入,因此需要先判断 i 和 j 的数位之和是否大于 k ,如果大于的话直接设置 vis[i][j] 为不可达即可。
否则,前面提到搜索方向只需朝下或朝右,因此 (i, j) 的格子只会从 (i - 1, j) 或者 (i, j - 1) 两个格子走过来(不考虑边界条件),那么 vis[i][j] 是否可达的状态则可由如下公式计算得到:
vis[i][j]=vis[i−1][j] or vis[i][j−1]
即只要有一个格子可达,那么 (i, j) 这个格子就是可达的,因此我们只要遍历所有格子,递推计算出它们是否可达然后用变量 ans 记录可达的格子数量即可。
class Solution {
int get(int x) {
int res=0;
for (; x; x /= 10){
res += x % 10;
}
return res;
}
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 (i - 1 >= 0) vis[i][j] |= vis[i - 1][j];// | = 按位或且赋值运算符
if (j - 1 >= 0) vis[i][j] |= vis[i][j - 1];
ans += vis[i][j];
}
}
return ans;
}
};
复杂度分析:
- 时间复杂度:O(mn),其中 m 为方格的行数, n 为方格的列数。一共有 O(mn)个状态需要计算,每个状态递推计算的时间复杂度为 O(1),所以总时间复杂度为 O(mn)。
- 空间复杂度:O(mn),其中 m 为方格的行数,n 为方格的列数。我们需要 O(mn)大小的结构来记录每个位置是否可达。