BFS三维标记例题以及注意要点
写在前面:
关于BFS的迷宫问题刷题刷到今天已经接近尾声了,对于比较复杂的迷宫问题特意选了两道比较有代表意义的例题写篇blog。两题的共通点就是走迷宫时的限制条件比较多,且用常规的vis二维数组标记无法解决重复遍历问题,这里引入了三维标记数组,也就是不仅记录坐标(x,y),还要记录到达(x,y)时的某个状态,这就要用到第三维标记,若是dp入过门的朋友会发现这里很像dp数组记录某个状态,以便后续到达此种状态时更新,这里只用于标记是否到达过这种状态。
N0.1
题意:
给定一个m*n大小的迷宫,#代表小怪,*代表通路,@代表起点,+代表终点,t为身上带的子弹数,每个子弹可消灭一个小怪,打怪不需要时间,每移动一次需一个单位时间,只能上下左右移动,问由起点至终点所需的最少时间是多少。
思路:
根据三维数组打标记的思路,可以加一维子弹数目的数组形成三维数组(x,y,剩余子弹数num),但需要注意的一点是在判断是否遍历过该结点的该状态是否之前遍历过时并非判断vis[tx][ty][nxt.num]是否为0,而是判断
vis[tx][ty][cur.num]是否为0。 可以这样理解,cur是以cur.num状态到达nxt点的,在当前的cur状态后再以与cur相同的状态cur.num到达nxt的点必然会比cur花费更多时间,因此在cur之后以相同状态到达nxt的点不必再进行搜索。当然,网上还有二维数组记录到过该点子弹最大数的做法,用了子弹数目不增的特性也可以解决,以及用优先队列每一次选择最佳点的解法,这两种方法在上一篇blog:“路上有打怪的BFS问题” 中有作重点讲解。但总的来看三维标记法更具有普适性。比如后面将要讲到的NO.2。
#include <bits/stdc++.h>
#include <iostream>
#define ll long long
#define mem( f, x ) memset( f, x, sizeof(f) )
#define INF 0x3f3f3f3f
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;
const int M = 205;
const int N = 205;
const int MAXN = 11;
char mp[N][N];
bool vis[N][N][MAXN];
int dir[4][2] = {
{
1, 0}, {
-1, 0}, {
0, 1}, {
0, -1} };
int m, n, t;
int sx, sy, ex, ey;
struct node{
int x, y;
int step, num;
node( ){
x = y = step = num = 0; }
node( int xx, int yy, int ss, int nn ){
x = xx, y = yy, step = ss, num = nn;
}
};
bool judge( int x, int y ){
return ( x >= 0 && y >= 0 && x < m && y < n );
}
void Bfs( ){
vis[sx][sy][0] = 1;
node cur = node( sx, sy, 0, t );
queue<node> q;
q.