一、题目
题目可以简化成这个样:
小蓝误入了一个混境之地,需要从起点走到出口。混境之地是一个矩阵,每个点有不同的高度,小蓝只能向上下左右走且必须从高处走到低处。小蓝有一次使用喷气背包的机会,可以让某个位置的高度提升。
二、解题思路
1、DFS遍历
根据从高到低的移动规则,从起点DFS遍历,把遍历过的地方统一标记一下(记忆化搜索)。倘若这一次遍历就可以到达终点,直接打印返回即可。
2、模拟使用一次喷气背包
把刚才标记过的地方统一增加k
的高度,然后在从起点,DFS遍历,倘若搜索到终点,那么打印"Yes",否则打印"No"。
这可能有同学会说了,不是说只能使用一次喷气背包吗?为什么给所有遍历过的地方都用一次呢?这不是违背题意吗?
实际上这种解题方式是一个等价替换。也就是说在某一个地方使用一次喷气背包的最终情况与直接把第一次遍历到的地方统一增高k
的高度,这两者的最终结果是一样的!
我们来看这张图:
对于第一次深搜到的地方(左边青绿色),在不使用喷气背包的情况下,也可以到达它的任何地方,只要在左半部分内。
但是右半部分未遍历到的地方
不同,那是因为高度太高,实在过不去,而我们通过统一增加遍历过的地方的高度(最大k
的高度),就可以模拟出使用一次喷气背包的情况了。
因为只要可以通过终点(使用一次喷气背包),那么在中间的黑色分界线中其中一个位置就可以成为连接两边的桥梁:
三、代码实现
1、设置好输入输出数据
import java.util.Scanner;
public class Main {
//分别表示 地图的长、宽,喷气背包的最大高度、起始点坐标(A,B) 终点坐标(C,D)
static int m,n,k,A,B,C,D;
//地图,用来记录每个位置的高度
static int[][] hight;
//用于上下左右移动
static int[] gX={0,0,-1,1};
static int[] gY={1,-1,0,0};
//用于标记当前位置是否被搜索过
boolean[][] visit;
public static void main(String[] args){
Scanner in=new Scanner(System.in);
m=in.nextInt();
n=in.nextInt();
k=in.nextInt();
//-1是因为计算机中数组下标是从0开始的
A=in.nextInt()-1;
B=in.nextInt()-1;
C=in.nextInt()-1;
D=in.nextInt()-1;
hight=new int[m][n];
visit=new boolean[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
graph[i][j]=in.nextInt();
}
}
}
}
2、定义深度搜索函数
private static void dfs(int x,int y){
//到达一个位置,就标记
visit[x][y]=true;
//当前位置的上下左右深度搜索
for(int i=0;i<4;i++){
int a=x+gX[i];
int b=y+gY[i];
//位置(a,b)不能越界,同时高度也不能比(x,y)大
if(a<0||b<0||a>=m||b>=n||hight[a][b]>hight[x][y])continue;
dfs(a,b);
}
}
3、第一次深度搜索+模拟使用喷气背包
public static void main(String[] args){
Scanner in=new Scanner(System.in);
m=in.nextInt();
n=in.nextInt();
k=in.nextInt();
//-1是因为计算机中数组下标是从0开始的
A=in.nextInt()-1;
B=in.nextInt()-1;
C=in.nextInt()-1;
D=in.nextInt()-1;
graph=new int[m][n];
visit=new boolean[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
graph[i][j]=in.nextInt();
}
}
//第一次深度搜索
dfs(A,B);
//可以直接到达,就不用走后续的流程了
if(visit[C][D]){
System.out.print("Yes");
return;
}
//模拟使用喷气背包
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(visit[i][j]){
hight[i][j]+=k;
//同时设置成false,为第二次深度搜索做准备
visit[i][j]=false;
}
}
}
}
4、第二次深度搜索
//第二次深度搜索
dfs(A,B);
System.out.print(visit[C][D]==true?"Yes":"No");
AC代码Java版
import java.util.Scanner;
public class Main {
//分别表示 地图的长、宽,喷气背包的最大高度、起始点坐标(A,B) 终点坐标(C,D)
static int m,n,k,A,B,C,D;
//地图
static int[][] hight;
//用于上下左右移动
static int[] gX={0,0,-1,1};
static int[] gY={1,-1,0,0};
//用于标记当前位置是否被搜索过
static boolean[][] visit;
public static void main(String[] args){
Scanner in=new Scanner(System.in);
m=in.nextInt();
n=in.nextInt();
k=in.nextInt();
//-1是因为计算机中数组下标是从0开始的
A=in.nextInt()-1;
B=in.nextInt()-1;
C=in.nextInt()-1;
D=in.nextInt()-1;
hight=new int[m][n];
visit=new boolean[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
hight[i][j]=in.nextInt();
}
}
dfs(A,B);
//可以直接到达,就不用走后续的流程了
if(visit[C][D]){
System.out.print("Yes");
return;
}
//模拟使用喷气背包
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(visit[i][j]){
hight[i][j]+=k;
//同时设置成false,为第二次深度搜索做准备
visit[i][j]=false;
}
}
}
//第二次深度搜索
dfs(A,B);
//第二次深度搜索
dfs(A,B);
System.out.print(visit[C][D]==true?"Yes":"No");
}
private static void dfs(int x,int y){
//到达一个位置,就标记
visit[x][y]=true;
//当前位置的上下左右深度搜索
for(int i=0;i<4;i++){
int a=x+gX[i];
int b=y+gY[i];
//位置(a,b)不能越界,同时高度也不能比(x,y)大
if(a<0||b<0||a>=m||b>=n||hight[a][b]>hight[x][y])continue;
dfs(a,b);
}
}
}