题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1429
题目大意:
魔王每t分钟回地牢视察一次,若发现Ignatius不在原位置便把他拎回去。只要在魔王下次视察之前走到出口就算离开地牢,如果魔王回来的时候刚好走到出口或还未到出口都算逃亡失败。
分析:
- 题目中明确说明有a~j共10把钥匙,迷宫大小为n*m,我们可以设置一个三维数组
status[MAXN][MAXN][1030]
表示出各种状态,其中第前两维用来表示地图中的每个点,第三维表示获得钥匙的状态。数组值为1,代表已经考虑过这种状态了,可跳过。为0,代表此状态没考虑过,需要加到队列中。 - 钥匙的状态可以用二进制表示,0代表暂未获得,1表示已经获得。最低位代表a号钥匙的获得状况,最高位表示j号钥匙的获得状况。
那么如果更新钥匙状态呢?如果走到mp[i][j] == 'c'
的位置,我们将 原有钥匙状态 与 将1左移'c'-'a'
位得到的数值 做或运算,这样对应位置的数变为1,代表已获得这一位对应的钥匙。
如何判断此门能否打开呢?如果走到mp[i][j] == 'C'
的位置,我们将 原始钥匙状态 和 将1左移'C-'A'
位得到的数值 做与运算,若原始钥匙状态的这位为1,证明有钥匙,可以打开此门。
附注释AC代码如下:
#include <iostream>
#include <queue>
#include <string.h>
using namespace std;
//HDU Accepted 1429 265MS 4160K 1642 B G++
const int MAXN = 24;
int ans = 0;
int sx, sy, ex, ey, n, m, t;
char mp[MAXN][MAXN];
int status[MAXN][MAXN][1030];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
struct node{
int x, y, t, key;
};
queue<node> q;
void bfs(int x, int y) {
bool flag = false;
node n1, n2;
n1.x = x, n1.y = y, n1.t = 0, n1.key = 0;
status[x][y][0] = 0;
while(!q.empty()) q.pop();
q.push(n1);
while(!q.empty()) {
if(flag == 1) //找到结果
break;
n2 = q.front();
q.pop();
for(int i=0; i<4; i++) {
int x_ = n2.x + dx[i];
int y_ = n2.y + dy[i];
int ky = n2.key;
if(x_<0 || x_>=n || y_<0 || y_>=m || mp[x_][y_] == '*') continue; //啊!因为边界写少了个等号,一直WA
if(mp[x_][y_] >= 'A' && mp[x_][y_] <= 'Z') {
//大门,位运算看有没有钥匙
int k = mp[x_][y_] - 'A';
if(!(ky & (1<<k)))
continue;
}
if(mp[x_][y_] >= 'a' && mp[x_][y_] <= 'z') {
int k = mp[x_][y_] - 'a';
ky = n2.key | (1<<k);
}
if(status[x_][y_][ky])
continue; //该状态访问过
status[x_][y_][ky] = 1; //标记
if(x_ == ex && y_ == ey) {
flag = 1;
ans = min(ans, n2.t + 1);
}
n1.x = x_, n1.y = y_;
n1.t = n2.t + 1;
n1.key = ky;
q.push(n1);
}
}
}
int main() {
while(cin>>n>>m>>t) {
memset(status, 0, sizeof(status));
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
cin>>mp[i][j];
if(mp[i][j] == '@')
sx = i, sy = j;
else if(mp[i][j] == '^')
ex = i, ey = j;
}
}
ans = 99999999;
bfs(sx, sy);
if(ans < t) cout<<ans<<endl;
else cout<<"-1"<<endl;
}
return 0;
}