NC14340逃脱
逃脱
https://ac.nowcoder.com/acm/problem/14340
题目描述
这是mengxiang000和Tabris来到幼儿园的第四天,幼儿园老师在值班的时候突然发现幼儿园某处发生火灾,而且火势蔓延极快,老师在第一时间就发出了警报,位于幼儿园某处的mengxiang000和Tabris听到了火灾警报声的同时拔腿就跑,不知道两人是否能够逃脱险境?
幼儿园可以看成是一个NM的图,在图中一共包含以下几种元素:
“.”:表示这是一块空地,是可以随意穿梭的。
“#”:表示这是一块墙,是不可以走到这上边来的,但是可以被火烧毁。
“S”:表示mengxiang000和Tabris所在位子。
“E”:表示幼儿园的出口。
“”表示火灾发源地(保证输入只有一个火灾发源地)。
已知每秒有火的地方都会向周围八个格子(上下左右、左上、右上、左下、右下)蔓延火势.mengxiang000和Tabris每秒都可以选择周围四个格子(上下左右)进行移动。(假设两人这一秒行动完之后,火势才蔓延开)
根据已知条件,判断两人能否成功逃脱险境,如果可以,输出最短逃离时间,否则输出T_T。
输入描述
第一行输入一个整数t,表示一共的测试数据组数。
第二行输入两个整数n,m,表示幼儿园的大小。
接下来n行,每行m个字符,表示此格子是什么元素。
t<=200
3<=n<=30
3<=M<=30
保证图中有一个起点,一个出口,一个火灾源处.
输出描述
每组数据输出一行,如果两人能够成功到达出口,那么输出最短逃离时间,否则输出T_T
题目分析1
1.数据存储方式:
通过结构体把图中的每个格子进行存储,包含格子的坐标,以及到达该格子的时间,并在创建时对其初始化。
struct Point{ int x,y,time; Point(){ time = 0; } Point(int a, int b, int c):x(a),y(b),time(c){} };
2.解题思路:
1)首先对fire进行一次bfs搜索,记录fire蔓延到每一个格子所需要的时间。
2)对人进行bfs搜索找到到达出口的最短消耗时间,注意如果在最短的消耗时间内也无法安全逃离,则无法逃离成功。
3)通过到达的时间与火势蔓延到该格子的时间进行比较,进行剪枝。
3.AC代码
#include <bits/stdc++.h> using namespace std; const int maxn = 35; int n,m,t,ans; ///ans 是正常出来的最短时间 char graph[maxn][maxn]; bool vis[maxn][maxn] = {0}; int fire[maxn][maxn] = {0}; ///记录fire到每一个格所需要时间 ///前四个是上下左右,后四个是左上,右上,左下,右下 int dir[][2] = { {1,0} , {-1,0} , {0,1} , {0,-1}, {1,1} , {-1,-1} , {-1,1} , {1,-1} }; struct Point{ int x,y,time; Point(){ time = 0; } Point(int a, int b, int c):x(a),y(b),time(c){} }fi,st; void bfs_fire(){ memset(vis,false,sizeof(vis)); memset(fire,0,sizeof(fire)); queue<Point> que; que.push(fi); vis[fi.x][fi.y] = true; while(!que.empty()){ Point head = que.front(); que.pop(); for (int i = 0;i < 8;i ++){ int nx = head.x + dir[i][0]; int ny = head.y + dir[i][1]; if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue; ///越界 if (vis[nx][ny]) continue; vis[nx][ny] = true; fire[nx][ny] = head.time + 1; ///计算每个点有或是蔓延到的时间 que.push({nx , ny , head.time + 1}); } } } bool bfs(){ memset(vis,0,sizeof(vis));///重新初始vis数组 queue<Point> que; que.push(st); vis[st.x][st.y] = true; while (!que.empty()){ Point head = que.front(); que.pop(); for (int i = 0;i < 4;i ++){ int nx = head.x + dir[i][0]; int ny = head.y + dir[i][1]; if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue; ///越界 if (vis[nx][ny]) continue; if (graph[nx][ny] == '#') continue; if (graph[nx][ny] == 'E' && head.time + 1 <= fire[nx][ny]) { ///人先到入口,火才到 ans = head.time + 1; return true; } if (head.time + 1 < fire[nx][ny]){ ///要严格小于,== 时本秒结束火就到了 vis[nx][ny] = true; que.push({nx , ny , head.time + 1}); } } } return false; } int main(){ cin >> t; while (t --){ cin >> n >> m; for (int i = 0;i < n;i ++){ for(int j = 0;j < m;j ++){ cin >> graph[i][j]; if (graph[i][j] == 'S'){ st.x = i; st.y = j; } else if (graph[i][j] == '*'){ fi.x = i; fi.y = j; } } } bfs_fire(); if (bfs()) cout << ans << endl; else cout << "T_T" << endl; } return 0; }
题目分析2
在网上浏览学习到了一个更简单的方法,采用切比雪夫距离进行求解,代码量直接减少一半(T_T),去掉了上述中的dfs_fire()方法。可能有很多和我一样,初次听到切比雪夫距离这个概念,所以首先简单的介绍一下原理:
切比雪夫距离:
指二个点之间的距离定义为其各座标数值差绝对值的最大值。以(x1,y1)和(x2,y2)二点为例,其切比雪夫距离为max(|x2-x1|,|y2-y1|)。下图展示了图中黄冠到各点的切比雪夫距离:
AC代码: 代码参考的博客
#include <bits/stdc++.h> using namespace std; const int maxn = 35; int n,m,t,ans; ///ans 是正常出来的最短时间 char graph[maxn][maxn]; bool vis[maxn][maxn] = {0}; ///上下左右 int dx[] = {0,0,1,-1}; int dy[] = {1,-1,0,0}; struct Point{ int x,y,time; Point(){ time = 0; } Point(int a, int b, int c):x(a),y(b),time(c){} }fi,st; int bfs(){ memset(vis,0,sizeof(vis)); queue<Point> que; que.push(st); vis[st.x][st.y] = true; while (!que.empty()){ Point head = que.front(); que.pop(); head.time ++; for (int i = 0;i < 4;i ++){ int nx = head.x + dx[i]; int ny = head.y + dy[i]; if (nx < 0 || nx >= n || ny < 0 || ny >= m || vis[nx][ny] || graph[nx][ny] == '#') continue; ///不满足条件的格子 越界、已访问过、墙壁 if(graph[nx][ny] == 'E') { //如果火到达终点所需要的时间,大于人走到这里的时间,说明能正常出去,返回时间 if(max(abs(nx - fi.x), abs(ny - fi.y)) >= head.time) return head.time; } if(max(abs(nx - fi.x), abs(ny - fi.y)) <= head.time) continue; vis[nx][ny] = true; que.push({nx, ny, head.time}); } } return -1; } int main(){ cin >> t; while (t --){ cin >> n >> m; for (int i = 0;i < n;i ++){ for(int j = 0;j < m;j ++){ cin >> graph[i][j]; if (graph[i][j] == 'S'){ st.x = i; st.y = j; } else if (graph[i][j] == '*'){ fi.x = i; fi.y = j; } } } int ans = bfs(); if (ans != -1) cout << ans << endl; else cout << "T_T" << endl; } return 0; }