一、题目描述
地上有一个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
二、思路分析
注:思路分析中的一些内容和图片参考自力扣各位前辈的题解,感谢他们的无私奉献
思路
本问题是典型的矩阵搜索问题,可使用深度优先搜索(DFS)+剪枝解决
深度优先搜索: 可以理解为暴力法模拟机器人在矩阵中的所有路径。DFS通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推
剪枝:在搜索中,遇到数位和超出目标值、此元素已访问,则应立即返回,称之为 可行性剪枝
可达解分析:
根据数位和增量公式得知,数位和每逢进位突变一次。根据此特点,矩阵中满足数位和的解构成的几何形状形如多个等腰直角三角形,每个三角形的直角顶点位于0, 10, 20, ...
等数位和突变的矩阵索引处。三角形内的解虽然都满足数位和要求,但由于机器人每步只能走一个单元格,而三角形间不一定是连通的,因此机器人不一定能到达,称之为不可达解。同理,可到达的解称为可达解(本题求此解)。
----图例展示了n,m=20, k∈[6,19]
的可达解、不可达解、非解以及连通性的变化
根据可达解的结构和连通性,易推出机器人可仅通过向右和向下移动,访问所有可达解。
----三角形内部:全部连通,易证
----两三角形连通处:若某三角形内的解为可达解,则必与其左边或上边的三角形连通(即相交),即机器人必可从左边或上边走进此三角形。
算法流程:
①首先分配一个二维矩阵visited
,用于记录各个节点是否被访问,所有元素初始值为0
②从起始点开始进行DFS,DFS的流程如下:
----判断当前行列是否越界、数位和是否超出目标值k、当前元素是否已经访问。如果满足其一,则返回0,代表不可达解
----如果都不满足,则将visited矩阵对应位置置为true
----设置回溯返回值,返回1+右方搜索的可达解总数+下方搜索的可达解总数
,代表从本单元格递归搜索的可达解总数
案例分析:
复杂度分析:
设矩阵行列数分别为M
、N
时间复杂度 O ( M N ) \rm{O(MN)} O(MN):最差情况下,机器人遍历矩阵所有单元格,此时时间复杂度为O(MN)
空间复杂度 O ( M N ) \rm{O(MN)} O(MN):需要使用一个二维的visited数组,使用O(MN)
的额外空间
三、整体代码
整体代码如下
//取出各位数并加起来
int compare_k(int i, int k){
int sum = 0;
while(i >0 ){
sum += i%10;
i/=10;
}
return sum;
}
int dfs(int m, int n, int k, int i, int j, int** visited){
/*
如果行或列越过边界、各位数值加起来大于K、当前位置的值已经被访问,则返回0,代表这一步走不了。
*/
if(i >= m || j >= n || compare_k(i, k) + compare_k(j, k) > k || visited[i][j] == 1) return 0;
visited[i][j] = 1; //如果可以走,则将这个点设置为已访问
//继续递归遍历右边和下边的点
return 1 + dfs(m, n, k, i + 1, j, visited) + dfs(m, n, k, i, j + 1, visited);
}
int movingCount(int m, int n, int k){
//分配二维的visited矩阵
int** visited = (int**)malloc(sizeof(int*)*m);
for (int i = 0; i < m; i++) {
visited[i] = (int *)calloc(n, sizeof(int));
}
//从起始点开始进行DFS
return dfs(m,n,k,0,0,visited);
}
运行,测试通过