题目表述
Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)……
这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带锁的门,钥匙藏在地牢另外的某些地方。刚开始Ignatius被关在(sx,sy)的位置,离开地牢的门在(ex,ey)的位置。Ignatius每分钟只能从一个坐标走到相邻四个坐标中的其中一个。魔王每t分钟回地牢视察一次,若发现Ignatius不在原位置便把他拎回去。经过若干次的尝试,Ignatius已画出整个地牢的地图。现在请你帮他计算能否再次成功逃亡。只要在魔王下次视察之前走到出口就算离开地牢,如果魔王回来的时候刚好走到出口或还未到出口都算逃亡失败。输入
每组测试数据的第一行有三个整数n,m,t(2<=n,m<=20,t>0)。接下来的n行m列为地牢的地图,其中包括:
. 代表路
* 代表墙
@ 代表Ignatius的起始位置
^ 代表地牢的出口
A-J 代表带锁的门,对应的钥匙分别为a-j
a-j 代表钥匙,对应的门分别为A-J
每组测试数据之间有一个空行。输出
针对每组测试数据,如果可以成功逃亡,请输出需要多少分钟才能离开,如果不能则输出-1。
不难看出,本题是一道搜索,而且本题需要记录钥匙的携带情况以判断门是否可以通过的问题
但是,有10种钥匙,这使维度处理变得困难(因为就算是开了10个维度记录钥匙也不能直接让某个维度改变,因为c++里就没有这种操作)
所以使用状态压缩
把10把钥匙的携带状态用一个二进制的数字记录下来(程序里用10进制整形变量表示)
例:第0位记录钥匙a的携带状态,0代表没有,1代表有。以此类推。
这样就可以带着10把钥匙的状态进行搜索了。
当遇到一把钥匙时:找到钥匙对应的位,然后和当前状态取“位或”操作就相当于捡起了这把钥匙
(位或:即按位取或,有1就得1,都不是1就得0)
例:当前状态有钥匙b,e;要捡起钥匙j
所以当前的状态:
a | b | c | d | e | f | g | h | i | j |
0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
在程序里就是18(2 ^ 1 + 2 ^ 4)
现在要捡起的钥匙j就是512(其他位置都是0,第九位是1)
a | b | c | d | e | f | g | h | i | j |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
两个二进制数字取位或之后
a | b | c | d | e | f | g | h | i | j |
0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
这样就算是捡起了钥匙。
同理,当遇到门时,判断是否有这个门对应的钥匙时
算出门对应的二进制数字,和当前状态取“位与”操作后,看得数是否为零,为零就是没有对应的钥匙。
(位与:按位取“与”,都为1得1,否则为0)
关于广搜:
先判断当前状态,再看能不能去,一定要有条理,否则会很乱。
并且广搜第一次到一个地方时一定是最小的步数(但深搜不是),所以再加个判断就行了
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
char a[25][25];
int n, m, t;
int f[25][25][2048];
int sx, sy;
int addx[5] = {0, -1, 0, 1, 0};
int addy[5] = {0, 0, 1, 0, -1};
struct point
{
int x;
int y;
int state;
};
queue <point> q;
int bfs()
{
int size;
point g, h;
g.x = sx;
g.y = sy;
g.state = 0;
f[g.x][g.y][g.state] = 0;
q.push(g);
while (q.size())
{
g = q.front();
q.pop();
for (int i = 1; i <= 4; i++)
{
h.x = g.x + addx[i];
h.y = g.y + addy[i];
h.state = g.state;
if (a[h.x][h.y] == '*')
continue;
if (a[h.x][h.y] >= 'a' && a[h.x][h.y] <= 'j') //捡钥匙,状态要变
{
size = a[h.x][h.y] - 'a';
h.state = g.state | (1 << size);
}
if (((a[h.x][h.y] >= 'a' && a[h.x][h.y] <= 'z') || a[h.x][h.y] == '.' ||
(a[h.x][h.y] >= 'A' && a[h.x][h.y] <= 'J' && (h.state & (1 << (a[h.x][h.y] - 'A'))) != 0)) //是钥匙 或者 路 或者 有对应钥匙能打开的门,但是一定要没去过
&& f[h.x][h.y][h.state] == -1)
{
f[h.x][h.y][h.state] = f[g.x][g.y][g.state] + 1;
q.push(h);
}
if (a[h.x][h.y] == '^')
if (f[g.x][g.y][g.state] + 1 < t)
return f[g.x][g.y][g.state] + 1;
else
return -1;
}
}
return -1;
}
int main()
{
int i, j;
while (cin >> n >> m >> t)
{
memset(f, -1, sizeof(f));
while (q.size())
q.pop();
for (i = 0; i <= n + 1; i++)
for (j = 0; j <= m + 1; j++)
a[i][j] = '*';
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++)
{
cin >> a[i][j];
if (a[i][j] == '@')
{
sx = i;
sy = j;
a[i][j] = '.';
}
}
cout << bfs() << endl;
}
return 0;
}