3502. 不同路径数
问题重述:
给定一个 n×m的二维矩阵,其中的每个元素都是一个 [1,9][1,9] 之间的正整数。
从矩阵中的任意位置出发,每次可以沿上下左右四个方向前进一步,走过的位置可以重复走。
走了 k次后,经过的元素会构成一个 (k+1) 位数。
请求出一共可以走出多少个不同的 (k+1) 位数。
输入格式
第一行包含三个整数 n,m,k。
接下来 n 行,每行包含 m 个空格隔开的整数,表示给定矩阵。
输出格式
输出一个整数,表示可以走出的不同 (k+1) 位数的个数。
数据范围
对于 30% 的数据, 1≤n,m≤2,0≤k≤2对于 100%100% 的数据,1≤n,m≤5,0≤k≤5,m×n>1输入样例:
3 3 2
1 1 1
1 1 1
2 1 1
输出样例:
5
样例解释
一共有 5 种可能的 3 位数:
111
112
121
211
212
问题分析:
本题要求计算不重复的最短路径,我们可以使用哈希表对路径进行记录,因为哈希表有不重复的特点,最后我们只需要输出哈希表的长度即为最短路径数。因为本题的数据范围比较小,我们可以使用深度优先算法对每一个点进行遍历,得到路径结果。
解法:
DFS深度优先
解题:
代码:
import java.util.HashSet;
import java.util.Scanner;
/**
* 给定一个 n×m 的二维矩阵,其中的每个元素都是一个 [1,9] 之间的正整数。
* 从矩阵中的任意位置出发,每次可以沿上下左右四个方向前进一步,走过的位置可以重复走。
* 走了 k 次后,经过的元素会构成一个 (k+1) 位数。
* 请求出一共可以走出多少个不同的 (k+1) 位数。
* 输入格式
* 第一行包含三个整数 n,m,k。
* 接下来 n 行,每行包含 m 个空格隔开的整数,表示给定矩阵。
* 输出格式
* 输出一个整数,表示可以走出的不同 (k+1) 位数的个数。
* 数据范围
* 对于 30% 的数据, 1≤n,m≤2,0≤k≤2
* 对于 100% 的数据,1≤n,m≤5,0≤k≤5,m×n>1
* 输入样例:
* 3 3 2
* 1 1 1
* 1 1 1
* 2 1 1
* 输出样例:
* 5
* 样例解释
* 一共有 5 种可能的 3 位数:
* 111
* 112
* 121
* 211
* 212
*/
public class Main {
static int n,m,k;
//定义一个set集合对路径进行保存,可以保证不出现重复
static HashSet<String> path = new HashSet<>();
//定义路径方向
static int [][]d = {{1,0},{-1,0},{0,1},{0,-1}};
//二维矩阵初始化
static int [][]arrays;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//输入三个整数
n = scanner.nextInt();
m = scanner.nextInt();
k = scanner.nextInt();
String str = "";
arrays = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
arrays[i][j] = scanner.nextInt();
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
//对每一个顶点使用深度优先算法
dfs(str,i,j);
}
}
System.out.println(path.size());
}
//深度优先
static void dfs(String str,int x,int y){
//若步数已经达到,或者超出范围则退出方法
if (str.length() == k+1){
path.add(str);
return;
}
//进行路径的计算
str +=arrays[x][y];
for (int i = 0; i < 4; i++) {
//开始,上下左右方向行进
x += d[i][0];
y += d[i][1];
if (x >= 0 && x < n && y >= 0 && y < m){
dfs(str,x,y);
}
//回退
x-=d[i][0];
y-=d[i][1];
}
}
}
代码分析:
首先定义一个HashSet,用于存储最短路径(存储字符串),同时对路径进行去重(因为HashSet内元素不能重复),定义一个二维数组用于存放上下左右方向x、y方向所需的变化量。定义三个整数,一个为行数,一个为每行的整数个数,一个为从某个顶点开始走的步数。定义一个二维数组,用于存储元素。定义一个dfs方法,参数为字符串(用于保存最短路径的字符串),以及当前所在顶点的位置(x、y)。方法内部,首先进行条件判断,如果最短路径所经过的步数已经达到了k,那么最短路径的长度应该为k+1,此时将该路径添加进哈希表中,同时退出方法。路径未达到最短,那么将当前所在顶点的数据添加进路径字符串尾部,因为每个顶点可以上下左右四个方向前进,每个顶点进行4次循环,更新x、y坐标,如果满足条件,那么调用dfs方法。进入方法后,继续进行条件的判断,不满足则更新路径以及x、y坐标再次调用dfs方法传入对应str、x、y,如果满足了条件,则退出当前方法,回到上一次执行该方法的时候,退出后代码应在当前位置
if (x >= 0 && x < n && y >= 0 && y < m){
dfs(str,x,y);
//退出时代码应到达这里
}
退出方法后,继续向下执行,因为此时的x、y是达到最短路径要求的最后一个顶点的位置,我们需要返回上一个顶点,所以对x、y进行回退更新操作(也就是减掉原来加上的值),继续未完成的循环,更改前进方向继续调用dfs方法。从二维数组的第一个顶点开始直到最后一个顶点结束,HashSet的长度即为最短路径的个数,输出长度即可。
深度优先遍历算法总结:
基本思想:按照某一指定方向,一直前进,直到没有路可走,则返回上一步,更改方向继续前进,没有路可走后在返回当前的上一步知道遍历完所有顶点。
dfs算法基本模板:
void dfs(){
//判断是否满足条件,满足条件则退出方法
if(){
对满足条件的记录进行保存
return;
}
//所需要走的方向,进行循环
for(){
//更新步数重新调用dfs方法进行递归,最好调用前进行条件判断,满足条件才调用
}
}