文章目录
- 题目
- 思路
- DFS
- 思路
- 代码
- 复杂度分析
- BFS
- 思路
- 代码
- 复杂度分析
题目
地上有一个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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof
思路
依然是典型的搜索+回溯的问题。可以选择用DFS也可以选择用BFS。
先讲两种方法都需要解决的问题——求数位和。
int sums(int x){int sum = 0;while(x != 0) {sum += x % 10;x /= 10;}return sum;
}
这里引入一个数位和增量公式,是我看作者:jyd的题解时学到的:
DFS
思路
DFS 通过递归,先朝一个方向搜到底(本题中应为右、下两个方向之一),再回溯至上个节点,沿另一个方向搜索。
可行性剪枝: 在搜索中,遇到数位和超出给定值、此元素已访问,则应立即返回。
判定元素是否被访问通过建立一个
m*n
的bool数组,访问过的位置标记为true,未访问的位置标记为false。
- 根据可行性剪枝确定终止条件:
- 下标越界
- 数位和超出给定值
- 元素已经被访问
此时返回0,搜索结果为不可达解。
- 递推工作:
- 将当前节点标记为true,表示已经访问过。
- 从当前节点的 下、右 两个方向开启下层递归。
- 回溯返回值:
返回 1 + 下方搜索的可达解总数 + 右方搜索的可达解总数,代表从本单元格递归搜索的可达解总数。
代码
class Solution {int rows;int cols;bool sumnumber(int i, int j, int k){int sum = 0;while(i != 0 || j != 0){if(i != 0){sum += i % 10;i /= 10;}if(j != 0){sum += j % 10;j /= 10;}}if(sum <= k){return true;}return false;}int dfs(int i, int j, vector<vector<bool>>& vb, int& k){bool flag = sumnumber(i, j, k);if(i >= rows || j >= cols || !flag || vb[i][j]){return 0;} vb[i][j] = true;return 1 + dfs(i + 1, j, vb, k) + dfs(i, j + 1, vb, k);}
public:int movingCount(int m, int n, int k) {vector<vector<bool>> vb(m, vector<bool>(n, 0));rows = m;cols = n;return dfs(0, 0, vb, k);}
};
复杂度分析
设矩阵行列数分别为 M, N
。
- 时间复杂度 O(MN): 最差情况下,机器人遍历矩阵所有单元格,此时时间复杂度为 O(MN)。
- 空间复杂度 O(MN): 最差情况下,
vector<vector<bool>> vb
内存储矩阵所有单元格的索引,使用 O(MN)的额外空间。
BFS
思路
两者都是遍历矩阵,但区别在于:
- DFS是向一个方向遍历到底,再回溯,不撞南墙不回头。
- BFS是平推的方式向前搜索。
BFS通常通过队列来实现。
- 初始化: 将初始点 (0, 0) 加入队列 queue ;
- 迭代终止条件: queue 为空。代表已遍历完所有可达解。
- 迭代过程:
- 单元格出队: 将队首单元格的
索引、数位和
弹出,作为当前搜索单元格。 - 判断是否跳过: 若
行列索引越界
数位和超出目标值 k
当前元素已访问过
时,执行 continue 。 - 标记当前单元格 : 将单元格索引 (i, j) 存入
辅助数组
中,代表此单元格已被访问过
。 - 单元格入队: 将当前元素的
下方、右方
单元格的索引、数位和
加入queue
。
- 返回值:
辅助数组
的长度即为可达解的数量。
代码
class Solution {bool sumnumber(int i, int j, int k){int sum = 0;while(i != 0 || j != 0){if(i != 0){sum += i % 10;i /= 10;}if(j != 0){sum += j % 10;j /= 10;}}if(sum <= k){return true;}return false;}
public:int movingCount(int m, int n, int k) {queue<pair<int,int>> que; // 临时存储可达解,为空则表示已经遍历完可达解vector<vector<bool>> vb(m, vector<bool>(n, 0)); // 记录访问过的节点vector<int> dx = {0,1}; // 向右、下的方向数组vector<int> dy = {1,0}; // 向右、下的方向数组int res = 0; // 可达解总数que.push(make_pair(0, 0));// 等价于 //que.push({ 0, 0 });while(!que.empty()){pair<int,int> x = que.front();que.pop();int i = x.first, j = x.second;bool flag = sumnumber(i, j, k);if(i >= m || j >= n || vb[i][j] || !flag) continue;vb[i][j] = true;res++;for(int a = 0; a < 2; a++){ // 当前单元格可达int tx = i + dx[a]; int ty = j + dy[a];que.push(make_pair(tx, ty));// 将右、下方向的单元格入队}}return res;}
};
复杂度分析
同DFS