BFS
POJ 2251 Dungeon Master
题意
有一个三维监狱,每一步可以向东,向西,向北,向南,向上,向下移动
‘S’为起点,'E’为出口,’#‘为墙,’.'为路
分析
找到起点,直接bfs即可
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
const int maxn = 32;
int k, n, m;
char a[maxn][maxn][maxn];
bool vis[maxn][maxn][maxn]; //记录是否经过
struct Node //存储点
{
int k;
int i;
int j;
int s; //时间
Node(int k, int i, int j, int s) :k(k), i(i), j(j), s(s){}
};
queue<Node> q;
int main() {
int _k[6] = { 0,0,0,0,-1,1 },
_i[6] = { -1,0,1,0,0,0 },
_j[6] = { 0,-1,0,1,0,0 }; //移动方案
while (~scanf("%d%d%d", &k, &n, &m) && k + n + m) {
int si = 0, sj = 0, sk = 0, ei = 0, ej = 0, ek = 0;
for (int K = 1; K <= k; K++) {
for (int i = 1; i <= n; i++) {
scanf("%s", a[K][i] + 1);
for (int j = 1; j <= m; j++) {
if (a[K][i][j] == 'S')
sk = K, si = i, sj = j;
}
}
}
while (!q.empty())q.pop();
q.push(Node(sk, si, sj, 0));
vis[sk][si][sj] = true;
int dk, di, dj, ans; bool flag = false;
while (!q.empty()) {
if (flag)break;
Node x = q.front(); q.pop();
for (int i = 0; i < 6; i++) {
dk = x.k + _k[i];
di = x.i + _i[i];
dj = x.j + _j[i];
if (dk<1 || dk>k || di<1 || di>n || dj<1 || dj>m)
continue;
if (vis[dk][di][dj])
continue;
if (a[dk][di][dj] == '#')
continue;
vis[dk][di][dj] = true;
q.push(Node(dk, di, dj, x.s + 1));
if (a[dk][di][dj] == 'E') {
flag = true;
ans = x.s + 1;
break;
}
}
}
if (flag) printf("Escaped in %d minute(s).\n", ans);
else printf("Trapped!\n");
memset(a, 0, sizeof(a));
memset(vis, 0, sizeof(vis));
}
}
HDU1429 胜利大逃亡(续)
bfs走回路
题意
有一副迷宫,在
(
<
t
)
(<t)
(<t)的时间内走到终点
. 代表路
- 代表墙
@ 代表Ignatius的起始位置
^ 代表地牢的出口
A-J 代表带锁的门,对应的钥匙分别为a-j
a-j 代表钥匙,对应的门分别为A-J
分析
不同于普通的bfs
有10把钥匙:考虑用bool数组,bitset,状压(即一个小于1024的数表示拿钥匙的情况,每个二进制位表示一把钥匙)
显然用状压更加灵活
走回路:由于会出现先去拿钥匙,再原路返回去开门的情况,所以在记录是否走过该点时要有所变通
一般是将vis数组改成记录一个值或者增加一维表示状态
显然通过一个点持有钥匙的情况有多种,只记录一种钥匙的情况是不够的,所以我们增加一维表示拿钥匙的情况
加下来就是普通的bfs
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
char s[30][30];
bool vis[30][30][1024];
struct Node
{
int i;
int j;
int key;
int s;
Node(int i,int j,int k,int s):i(i),j(j),key(k),s(s){}
};
queue<Node> q;
int main() {
int n, m, t;
int si = 0, sj = 0;
int _i[4] = { -1,0,1,0 },
_j[4] = { 0,-1,0,1 };
while (~scanf("%d%d%d", &n, &m, &t)) {
for (int i = 1; i <= n; i++) {
scanf("%s", s[i] + 1);
for (int j = 1; j <= m; j++)
if (s[i][j] == '@')
si = i, sj = j;
}
q.push(Node(si, sj, 0, 0));
vis[si][sj][0] = true;
int di, dj, ans;
bool flag = false;
while (!q.empty()) {
if (flag)break;
Node x = q.front(); q.pop();
if (x.s == t - 1)break;
for (int i = 0; i < 4; i++) {
di = x.i + _i[i];
dj = x.j + _j[i];
if (di<1 || di>n || dj<1 || dj>m)
continue;
if (s[di][dj] == '*')
continue;
if (s[di][dj] >= 'A' && s[di][dj] <= 'J')
if (!(x.key >> (s[di][dj] - 'A') & 1))
continue;
int key = x.key;
if (s[di][dj] >= 'a' && s[di][dj] <= 'j')
key |= (1 << (s[di][dj] - 'a'));
if (vis[di][dj][key])
continue;
vis[di][dj][key] = true;
q.push(Node(di, dj, key, x.s + 1));
if (s[di][dj] == '^') {
flag = true;
ans = x.s + 1;
break;
}
}
}
memset(vis, 0, sizeof(vis));
if (flag) printf("%d\n", ans);
else printf("-1\n");
while (!q.empty())q.pop();
}
}
双向广搜
对于给定了起始状态和终止状态,且正逆搜索均能够实现的情况
将起始状态和终止状态同时作为起点进行 BFS,相对于单向 BFS 可以减少相当一部分搜索量
对于扩展结点较多,而目标结 点又处在深处的情况有很好的优化效果
我们将起点和终点同时加入队列进行 BFS
对于起点和终点扩展 出的点我们打上不同标记
当一个点即将打上第二个标记的时候 说明双向搜索相遇,输出步数之和即可
while (qx.size()) {
int x = qx.front(), y = qy.front();
qx.pop(), qy.pop();
for (int i = 0; i < 8; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (nx >= n || ny >= n || nx < 0 || ny < 0) continue;
if (vis[nx][ny]) {
if (vis[nx][ny] == vis[x][y]) continue;
return step[x][y] + step[nx][ny] + 1;
}
step[nx][ny] = step[x][y] + 1;
vis[nx][ny] = vis[x][y];
qx.push(nx), qy.push(ny);
}
}
POJ3182 The Grove
[USACO06JAN]树林The Grove
题意
给出一张图,从起点出发,将‘x’连通块圈住,返回起点,所需最小步数
分析
不同于其他bfs,本题所需从起点出发回到起点
射线法:为了防止不经过圈,就回到起点
- 从要圈的部分任意一点引一条向下的射线,必须要穿过射线一次
回到原点才是完成圈地 - 为防止反复穿过射线,我们需要对出穿过射线做一个方向限制
如果当前在射线上,且企图抵达反向穿越射线的点则 continue
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
#pragma warning (disable:4996)
const int maxn = 55;
struct Node
{
int x;
int y;
int s;
bool cross;
Node(int x, int y, bool cross, int s) :x(x), y(y), cross(cross), s(s) {}
};
queue <Node> q;
char s[maxn][maxn];
bool mark[maxn][maxn], vis[maxn][maxn][2];
int n, m, _X, _Y;
void MarkLine() { //画出射线
bool flag = true;
for (int i = 1; i <= n && flag; i++)
for (int j = 1; j <= m && flag; j++)
if (s[i][j] == 'X'){
_X = i, _Y = j;
flag = false;
break;
}
for (int i = _X - 1; i >= 1; i--)
mark[i][_Y] = true;
}
int main() {
int _x[8] = { -1,0,1,0,-1,-1,1,1 },
_y[8] = { 0,-1,0,1,-1,1,-1,1 };
int sx, sy;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%s", s[i] + 1);
for (int j = 1; j <= m; j++)
if (s[i][j] == '*')
sx = i, sy = j;
}
MarkLine();
q.push(Node(sx, sy, false, 0));
vis[sx][sy][0] = true;
int dx, dy, ans; bool flag = false, g;
while (!q.empty()) {
if (flag) break;
Node x = q.front(); q.pop();
for (int k = 0; k < 8; k++) {
dx = x.x + _x[k];
dy = x.y + _y[k];
g = x.cross | mark[dx][dy];
if (dx<1 || dx>n || dy<1 || dy>m)
continue;
if (s[dx][dy] == 'X')
continue;
if (vis[dx][dy][g])
continue;
if (mark[dx][dy] && x.y >= _Y) //只能从左向右经过射线
continue;
if (mark[x.x][x.y] && dy <= _Y) //经过射线向右移动
continue;
vis[dx][dy][g] = true;
q.push(Node(dx, dy, g, x.s + 1));
if (s[dx][dy] == '*') {
ans = x.s + 1;
flag = true;
break;
}
}
}
printf("%d\n", ans);
}
UVA10047 The Monocycle
题意
独轮车是一种仅有一个轮子的特殊自行车。他的轮子被等分成5个扇形,分别涂上一种不同的颜色。现在有一个人骑自行车行驶在M*N的网格平面上。每个格子的大小恰好使得当车从一个格子骑到下一个格子时,轮子恰好转过一个扇形。
如下图所示,当轮子在1号格子的中心时,蓝色扇形的外弧线中线刚好于地面接触。当它移动到下一个格子(2号格子)的时候,白色扇形的外弧线于地面接触。
有些各自中有障碍,所以车子不能通过这些格子。骑车人从某个格子出发,希望用最短的时间移动到目标格。在任何一个格子上,他要么骑到下一个格子,要么左转或者右转90度。其中每项动作恰好需要1秒来完成。初始时,他面朝北且绿色扇形贴着地面。到达目标格时,也必须是绿色扇形贴着地面,但朝向无限制。如下图所示。
分析
显然的bfs,不同于其他bfs就是参数比较多
码量不大,仔细一点就好
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
int n, m;
char a[30][30];
bool vis[30][30][4][5];
struct Node
{
int x;
int y;
int s;
int c;
int d;
Node(int x,int y,int d,int c,int s):x(x),y(y),d(d),c(c),s(s){}
};
queue<Node>q;
int main() {
int sx, sy, g = 0;
int _x[6] = { -1,0,1,0,0,0 },
_y[6] = { 0,1,0,-1,0,0 },
_d[6] = { 0,0,0,0,1,-1 },
_c[6] = { 1,1,1,1,0,0 };
while (~scanf("%d%d", &n, &m) && n + m) {
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i++) {
scanf("%s", a[i] + 1);
for (int j = 1; j <= m; j++)
if (a[i][j] == 'S')
sx = i, sy = j;
}
q.push(Node(sx, sy, 0, 0, 0));
vis[sx][sy][0][0] = true;
int dx, dy, dd, dc, ans;
bool flag = false;
while (!q.empty()) {
if (flag)break;
Node x = q.front(); q.pop();
for (int k = 3; k < 6; k++) {
if (k == 3) {
dx = x.x + _x[x.d];
dy = x.y + _y[x.d];
dd = x.d;
dc = (x.c + _c[x.d]) % 5;
}
else {
dx = x.x;
dy = x.y;
dd = (x.d + _d[k] + 4) % 4;
dc = x.c;
}
if (dx<1 || dx>n || dy<1 || dy>m)
continue;
if (vis[dx][dy][dd][dc])
continue;
if (a[dx][dy] == '#')
continue;
vis[dx][dy][dd][dc] = true;
q.push(Node(dx, dy, dd, dc, x.s + 1));
if (a[dx][dy] == 'T' && dc == 0) {
flag = true;
ans = x.s + 1;
break;
}
}
}
while (!q.empty())q.pop();
if (g)printf("\n");
printf("Case #%d\n", ++g);
if (flag)printf("minimum time = %d sec\n", ans);
else printf("destination not reachable\n");
}
}