献给阿尔吉侬的花束
- 1.题目
- 2.基本思想
- 3.代码实现
1.题目
阿尔吉侬是一只聪明又慵懒的小白鼠,它最擅长的就是走各种各样的迷宫。
今天它要挑战一个非常大的迷宫,研究员们为了鼓励阿尔吉侬尽快到达终点,就在终点放了一块阿尔吉侬最喜欢的奶酪。
现在研究员们想知道,如果阿尔吉侬足够聪明,它最少需要多少时间就能吃到奶酪。迷宫用一个 R × C R×C R×C 的字符矩阵来表示。
字符 S S S 表示阿尔吉侬所在的位置,字符 E E E 表示奶酪所在的位置,字符 # 表示墙壁,字符 . 表示可以通行。
阿尔吉侬在 1 个单位时间内可以从当前的位置走到它上下左右四个方向上的任意一个位置,但不能走出地图边界。
输入格式
第一行是一个正整数 T,表示一共有 T组数据。
每一组数据的第一行包含了两个用空格分开的正整数 R 和 C,表示地图是一个 R×C的矩阵。接下来的 R 行描述了地图的具体内容,每一行
包含了 C 个字符。字符含义如题目描述中所述。保证有且仅有一个 S 和 E。
输出格式
对于每一组数据,输出阿尔吉侬吃到奶酪的最少单位时间。
若阿尔吉侬无法吃到奶酪,则输出“oop!”(只输出引号里面的内容,不输出引号)。
每组数据的输出结果占一行
数据范围
1
<
T
≤
10
1<T≤10
1<T≤10,
2
≤
R
,
C
≤
200
2≤R,C≤200
2≤R,C≤200
输入样例:
输出样例:
5
1
oop!
2.基本思想
BFS 宽搜:每次取出队头元素,然后将该队头元素拓展出的所有元素放到队尾
- 需要的数组:
- 1.判重数组:
st[]
一般是入队时判重(这样可以保证每一个点入队一次) - 2.
queue
队列
宽搜一般模板
queue <- 初始状态入队 //先将某个初始状态入队
while(queue 非空)
{
t <- 队头 //每次取出队头元素放到t中
for(扩展t) //扩展t结点
{
ver <- 新节点
if(! st[ver]) //入队判重
{
ver -> 队尾 //新节点插入队尾
}
}
}
BFS能找到 最小的步数,即 最短路径 ,而 DFS 找到的不一定最短。
因为BFS是按层来遍历的,会先把所有距离为0的点遍历完,然后再遍历所有距离为1的点,按这样的顺序来遍历的,再遍历所有距离为2的点,一层一层往外扩展,因此第一次扩展到终点时,必然是最短距离
3.代码实现
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
public class _1101献给阿尔吉侬的花束 {
static class PII { //保存坐标
int x, y;
public PII(int x, int y) {
this.x = x;
this.y = y;
}
}
static final int N = 210;
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static char[][] g = new char[N][N];//保存迷宫信息
static int[][] dis = new int[N][N];//dis 存储起点到每个点的路径长度 同时当作判重 数组
static int r, c;
public static void main(String[] args) throws IOException {
int T = Integer.parseInt(br.readLine());
while (T-- > 0) {
String[] s = br.readLine().split(" ");
r = Integer.parseInt(s[0]);
c = Integer.parseInt(s[1]);// r 行 每行 c 个字符
for (int i = 0; i < r; i++) {//一次读取一行
g[i] = br.readLine().toCharArray();
}
PII start = null, end = null;//地图起点 终点
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
//枚举 地图找出 起点 终点
if (g[i][j] == 'S') start = new PII(i, j);
else if (g[i][j] == 'E') end = new PII(i, j);
}
}
int distance = bfs(start, end);
if (distance == -1) System.out.println("oop!");
else System.out.println(distance);
}
}
private static int bfs(PII start, PII end) {//BFS
Queue<PII> queue = new LinkedList<>();//建立一个队列
for (int i = 0; i < r; i++) Arrays.fill(dis[i], -1);//先把距离都初始化为-1,-1表示不可到达
dis[start.x][start.y] = 0;//表示起点已经走过了,且距离起点为0
queue.offer(start);//将起点放入队列
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};//建立数组 方便找到 上下左右 四个方向的元素
while (!queue.isEmpty()) {//当队列非空
PII t = queue.poll();//取出对头元素 并删除
for (int i = 0; i < 4; i++) {//循环,让该点往上下左右四个方向走,扩展该点
int x = t.x + dx[i], y = t.y + dy[i];//坐标走到相应的地方
if (x < 0 || x >= r || y < 0 || y >= c) continue;//出界
if (g[x][y] == '#') continue;//障碍物
if (dis[x][y] != -1) continue;//之前已经遍历
dis[x][y] = dis[t.x][t.y] + 1;//如果能走到,则这个点距离起点的距离是上一个点距离起点的距离+1
if (end.x == x && end.y == y) return dis[x][y];//如果当前已经走到终点,直接返回当前dis
//没结束 继续
queue.offer(new PII(x, y));
}
}
return -1;
}
}