Saving Tang Monk

问题分析

本题就是属于拿钥匙去开门的bfs b f s 类题目。
题意:孙悟空必须拿到n n 个钥匙才能救出唐僧,但是期间如果遇到蛇怪需要多花一分钟去打败她们,并且在拿到第ii个钥匙之前必须先拿到第i−1 i − 1 个钥匙,问最少的花费时间。

这题就麻烦在三个地方,
一、同种钥匙可能有多个,拿完钥匙的地方仍然可以走,所以需要加一维钥匙的状态来判重。
二、因为打败蛇怪需要多花费时间,所以肯定要优先走不经过蛇怪的最短路,优先队列解决。
三、打过蛇怪的地方仍然可以走动,所以需要判断蛇洞里的蛇怪dead d e a d 没有,所以需要节点加一个打蛇怪的状态(假设为snake) ( 假 设 为 s n a k e ) 。就比如用0,1,2,3,4... 0 , 1 , 2 , 3 , 4... 给蛇编号的,判断i i 号蛇怪是否死亡就用当前的状态snake&(1<<i)snake&(1<<i),如果结果等于0则说明i蛇怪还没有dead d e a d ,否则就是dead d e a d 。

#include <bits/stdc++.h>

struct node {
int x, y, step, snake, key;

bool operator<(const node &u) const {
return step > u.step;
}
};

const int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
bool vis[111][111][11];
char a[111][111];
int n, m, snakeLoc[111][111];

int rec(int stx, int sty, int desx, int desy) {
std::priority_queue<node> pq;
node cur, nxt;
cur.x = stx, cur.y = sty, cur.step = cur.key = cur.snake = 0;
pq.push(cur);
while (!pq.empty()) {
cur = pq.top();
pq.pop();
if (cur.x == desx && cur.y == desy && cur.key == m)
return cur.step;
for (int i = 0; i < 4; ++i) {
int tx = cur.x + dir[i][0];
int ty = cur.y + dir[i][1];
int key = cur.key, step = cur.step + 1, snake = cur.snake;
if (a[tx][ty] == cur.key + '1')
key++;
if (tx < 0 || tx >= n || ty < 0 || ty >= n || vis[tx][ty][key] || a[tx][ty] == '#')
continue;
if (a[tx][ty] == 'S' && ((snake & (1 << snakeLoc[tx][ty])) == 0)) //if it is a alive snake
step++, snake |= (1 << snakeLoc[tx][ty]);
vis[tx][ty][key] = 1;
nxt = {tx, ty, step, snake, key};
pq.push(nxt);
}
}
return -1;
}

int main() {
while (~scanf("%d%d", &n, &m) && (n + m)) {
int stx, sty, desx, desy, cnt = 0;
memset(vis, 0, sizeof vis);
memset(snakeLoc, 0, sizeof(snakeLoc));
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j) {
scanf(" %c", &a[i][j]);
if (a[i][j] == 'K') stx = i, sty = j;
if (a[i][j] == 'T') desx = i, desy = j;
if (a[i][j] == 'S') snakeLoc[i][j] = cnt++;
}
int ans = rec(stx, sty, desx, desy);
if (ans == -1) puts("impossible");
else printf("%d\n", ans);
}
return 0;
}