Nightmare Ⅱ(双向BFS)

Problem Description

Last night, little erriyue had a horrible nightmare. He dreamed that he and his girl friend were trapped in a big maze separately. More terribly, there are two ghosts in the maze. They will kill the people. Now little erriyue wants to know if he could find his girl friend before the ghosts find them.
You may suppose that little erriyue and his girl friend can move in 4 directions. In each second, little erriyue can move 3 steps and his girl friend can move 1 step. The ghosts are evil, every second they will divide into several parts to occupy the grids within 2 steps to them until they occupy the whole maze. You can suppose that at every second the ghosts divide firstly then the little erriyue and his girl friend start to move, and if little erriyue or his girl friend arrive at a grid with a ghost, they will die.

Note: the new ghosts also can devide as the original ghost.

Input

The input starts with an integer T, means the number of test cases.

Each test case starts with a line contains two integers n and m, means the size of the maze. (1<n, m<800)

The next n lines describe the maze. Each line contains m characters. The characters may be:

‘.’ denotes an empty place, all can walk on.

‘X’ denotes a wall, only people can’t walk on.

‘M’ denotes little erriyue

‘G’ denotes the girl friend.

‘Z’ denotes the ghosts.

It is guaranteed that will contain exactly one letter M, one letter G and two letters Z.

Output

Output a single integer S in one line, denotes erriyue and his girlfriend will meet in the minimum time S if they can meet successfully, or output -1 denotes they failed to meet.

SampleInput

3
5 6
XXXXXX
XZ..ZX
XXXXXX
M.G...
......
5 6
XXXXXX
XZZ..X
XXXXXX
M.....
..G...

10 10
..........
..X.......
..M.X...X.
X.........
.X..X.X.X.
.........X
..XX....X.
X....G...X
...ZX.X...
...Z..X..X

SampleOutput

1
1
-1

题意就是给你一个迷宫,不对,就是说你现在被困在迷宫里,要去和你GF见面,但是迷宫中还有人不可以走的墙和会杀死人的幽灵,幽灵每个单位时间都会往上下左右延申新的幽灵。幽灵有两个,M是你的位置,G是GF的位置。幽灵每秒可以延申两格,你可以每秒走三步,GF每秒只能走一步,如果在不被杀死的情况和女朋友汇合就输出最小单位时间,否则输出-1.(注意幽灵是可以往墙上延申的)因为两个人都可以动,不用说,双向BFS是肯定的,不过有一点就是,如何实现每秒走三步以及判断是否被杀死呢?答案是我也不知道。这道题目就留给各位自己思考。=7=嘻嘻 不闹了。其实可以换个思路去思考,我们可以用三个bfs代替走三步,但是如何判断是否被杀死呢,其实这道题可以不需要判断是否被杀死,而是判断人物走到能否在幽灵延申到某个位置之前走到那个位置,用曼哈顿距离判断就行了。

提一下什么是曼哈顿距离常用距离度量方法有十一种,而我们大部分时间只用到欧氏距离和曼哈顿距离。设两个点的坐标(X1,Y1),(X2,Y2);欧氏距离就是坐标的直线距离 = sqrt((X2 - X1)2+(Y2 - Y1)2)而曼哈顿距离就是以欧式距离为斜边构造直角三角形的两直角边和 = |X2 - X1| + |Y2 - Y1|为什么有这么多构造方式以及其区别,这篇博文就不详细介绍了。

值得一题的是,因为是双向搜索,所以需要开两个二维数组或是一个三维数组分别标记你或者GF是否走过。还有就是代码BFS中的
1 int len = q[w].size();
2 while(len--)

因为我们不是一次就搜索完,我们的BFS仅仅只是做走一步的作用,所以只把当前已经存的点的下一点存入就行了。若是觉得难以理解可以替换成while(!q[w].empty)观察每一步的输出情况。

代码:
  1 #include <iostream>
  2 #include <string>
  3 #include <cstdio>
  4 #include <cstdlib>
  5 #include <sstream>
  6 #include <iomanip>
  7 #include <map>
  8 #include <stack>
  9 #include <deque>
 10 #include <queue>
 11 #include <vector>
 12 #include <set>
 13 #include <list>
 14 #include <cstring>
 15 #include <cctype>
 16 #include <algorithm>
 17 #include <iterator>
 18 #include <cmath>
 19 #include <bitset>
 20 #include <ctime>
 21 #include <fstream>
 22 #include <limits.h>
 23 #include <numeric>
 24
 25 using namespace std;
 26
 27 #define F first
 28 #define S second
 29 #define mian main
 30 #define ture true
 31
 32 #define MAXN 1000000+5
 33 #define MOD 1000000007
 34 #define PI (acos(-1.0))
 35 #define EPS 1e-6
 36 #define MMT(s) memset(s, 0, sizeof s)
 37 typedef unsigned long long ull;
 38 typedef long long ll;
 39 typedef double db;
 40 typedef long double ldb;
 41 typedef stringstream sstm;
 42 const int INF = 0x3f3f3f3f;
 43
 44 int fx[4][2]={1,0,-1,0,0,1,0,-1};
 45 char mp[810][810];
 46 int vis[2][810][810];
 47 int gx,gy,mx,my,n,m,step;  //记录坐标,这里的step指的是GF走的步数,应该理解成走了多少单位时间
 48 pair<int,int>cur,z[2];  //z用来记录幽灵位置
 49 queue<pair<int,int> >q[2];    //分别记录你和GF的路径
 50
 51 bool check(pair<int,int> x){
 52     if(x.F < 0 || x.S < 0 || x.F >= n || x.S >= m || mp[x.F][x.S] == ‘X‘)
 53         return false;
 54     if((abs(x.F-z[0].F)+abs(x.S-z[0].S)) <= 2*step || (abs(x.F-z[1].F)+abs(x.S-z[1].S)) <= 2*step)    //判断在幽灵延申到某个点之前是否能走到
 55         return false;
 56     return true;
 57 }
 58
 59 int bfs(int w){
 60     pair<int,int>tp,next;
 61     int len = q[w].size();
 62     while(len--){  //注意这里不是搜完,因为是多次搜索,只需要把当前步骤行进完就行了
 63         tp = q[w].front();
 64         q[w].pop();
 65         if(!check(tp)) continue;
 66         for(int i = 0; i < 4; i++){
 67             next.F = tp.F + fx[i][0];
 68             next.S = tp.S + fx[i][1];
 69             if(!check(next))
 70                 continue;
 71             if(!vis[w][next.F][next.S]){
 72                 if(vis[1-w][next.F][next.S])    //判断下一个点是否对方已经走过
 73                     return 1;
 74                 vis[w][next.F][next.S] = 1;
 75                 q[w].push(next);
 76             }
 77         }
 78     }
 79     return 0;
 80 }
 81
 82 int solve(){
 83     while(!q[0].empty())
 84         q[0].pop();
 85     while(!q[1].empty())
 86         q[1].pop();
 87
 88     cur.F = mx;
 89     cur.S = my;
 90     q[0].push(cur);
 91     cur.F = gx;
 92     cur.S = gy;
 93     q[1].push(cur);
 94     MMT(vis);
 95     vis[0][mx][my] = vis[1][gx][gy] = 1;
 96     step = 0;
 97
 98     while((!q[0].empty()) || (!q[1].empty())){
 99         step++;
100         if(bfs(0))    //通过三次bfs达到走三步
101             return step;
102         if(bfs(0))
103             return step;
104         if(bfs(0))
105             return step;
106         if(bfs(1))
107             return step;
108     }
109     return -1;
110 }
111
112 int main(){
113     ios_base::sync_with_stdio(false);
114     cout.tie(0);
115     cin.tie(0);
116     int t;
117     cin>>t;
118     while(t--){
119         int cnt = 0;
120         cin>>n>>m;
121         for(int i = 0; i < n; i++)
122             cin>>mp[i];
123         for(int i = 0; i < n; i++)
124             for(int j = 0; j < m; j++){
125                 if(mp[i][j] == ‘G‘)
126                     gx = i, gy = j;
127                 if(mp[i][j] == ‘M‘)
128                     mx = i, my = j;
129                 if(mp[i][j] == ‘Z‘)
130                     z[cnt].F = i, z[cnt++].S = j;
131             }
132         cout << solve() << endl;
133     }
134     return 0;
135 }


原文地址:https://www.cnblogs.com/xenny/p/9387961.html

时间: 2024-10-11 17:43:57

Nightmare Ⅱ(双向BFS)的相关文章

HDU3085 Nightmare Ⅱ (双向BFS)

联赛前该练什么?DP,树型,状压当然是爆搜啦 双向BFS就是两个普通BFS通过一拼接函数联系,多多判断啦 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define R(a,b,c) for(register int a = (b); a <= (c); ++a) #define nR(a,b,c

Hdu1401-Solitaire(双向bfs)

Solitaire is a game played on a chessboard 8x8. The rows and columns of the chessboard are numbered from 1 to 8, from the top to the bottom and from left to right respectively.There are four identical pieces on the board. In one move it is allowed to

UVA-1604 Cubic Eight-Puzzle (双向BFS+状态压缩+限制搜索层数)

题目大意:立体的八数码问题,一次操作是滚动一次方块,问从初始状态到目标状态的最少滚动次数. 题目分析:这道题已知初始状态和目标状态,且又状态数目庞大,适宜用双向BFS.每个小方块有6种状态,整个大方格有9*6^8个状态.每个小方块用一位6进制数表示即可. 注意:状态转移时要谨慎,否则会出现意想不到的错误: 这道题的末状态有256(2^8)个,如果对搜索层数不加限制,即使双向BFS也会TLE的,当限制正向搜索15层逆向搜索15层至正向搜索27层反向搜索3层时都能AC(我下面贴出的程序是这样的),其

HDU1195 双向BFS(或BFS)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1195 , 双向BFS或者直接BFS也可以过. 其实这道题只是单向BFS就可以过的,但是为了练算法,所以还是用了双向BFS来写. 算法: 先预处理一下,从1111到9999的所有点进行构图(由于是1~9的,所以除去含有0元素的数字),能进行一次变换变成的数字则表示两点之间连通.然后从初态与目态两个点进行BFS,如果有轨迹重合的就返回路程和. 这里注意双向BFS要一层一层的进行搜索,不然的话会产生错误,

POJ1915Knight Moves(单向BFS + 双向BFS)

题目链接 单向bfs就是水题 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <queue> 6 using namespace std; 7 const int INF = 0x3f3f3f3f; 8 const int Max = 300 + 5; 9 struct Node 10 { 11 int

BFS、双向BFS和A*

BFS.双向BFS和A* Table of Contents 1. BFS 2. 双向BFS 3. A*算法 光说不练是无用的.我们从广为人知的POJ 2243这道题谈起:题目大意:给定一个起点和一个终点.按骑士的走法(走日字),从起点到终点的最少移动多少次 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2RraXJjaGhvZmY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/g

HDU 1043 Eight(双向BFS+康托展开)

http://acm.hdu.edu.cn/showproblem.php?pid=1043 题意:给出一个八数码,求出到达指定状态的路径. 思路:路径寻找问题.在这道题里用到的知识点挺多的.第一次用双向BFS来做. ①双向BFS 在单向BFS的基础上,多建一个从终止状态开始搜索的队列,当然这个时候需要两个vis[]辅助数组,分别记录两个队列的访问情况,当两个队列相遇时即可终止循环. ②康托展开 X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[

POJ 1915-Knight Moves (单向BFS &amp;&amp; 双向BFS 比较)

题目链接:Knight Moves 研究了一下双向BFS,不是很难,和普通的BFS一样,双向BFS不过是从 起点和终点同时开始搜索,可减少搜索时间 当两条搜索路线相遇时,结束. 貌似有一年百度的招聘 笔试,就是双向BFS.... 下面,比较一下BFS 和 双向BFS的用时: BFS STL的queue可能会浪费一点时间 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstrin

HDU 1072 Nightmare (BFS)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1072 题目大意: 走迷宫,初始剩余时间为6min,每步1min:到reset区是若剩余时间大于0,则可以重置.到终点3区,若时间大于0,则成功逃脱.(可以走回路) 0:wall 1:可以走 2:起点 3:终点 4:剩余时间重置为6 源代码: #include<iostream> #include<cstring> #include<cstdio> #include<q