题目链接
题意
一个有n*m个方格的迷宫,相邻的方格之间可能是墙,也可能是连通的,也可能是门,门有多种类型,需要对应类型的钥匙才可以通过,某些方格里存放着钥匙,问从左上角(1,1)到(n,m)最短的时间。
思路
分层图,以携带的钥匙为层数,用二进制法存,按题意建图后跑spfa。
#include
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 2e2 + 7;
const int maxm = 5e3 + 7;
typedef long long ll;
int key[maxn * maxn], dis[maxm][maxn];
int id[maxn][maxn], head[maxn * maxn], mp[maxn][maxn], to[maxn*maxn], nex[maxn*maxn], edge[maxn*maxn];
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
struct node {
int x, v;
node(int a, int b): x(a), v(b){}
};
int n, m, s, t, cnt;
void init()
{
cnt = 0; s = 1; t = n * m;
memset(head, -1, sizeof(head));
memset(mp, -1, sizeof(mp));
memset(key, 0, sizeof(key));
}
void add(int x, int y, int w)
{
to[cnt] = y;
edge[cnt] = w;
nex[cnt] = head[x];
head[x] = cnt++;
}
int spfa()
{
queue<node> que;
memset(dis, INF, sizeof(dis));
dis[1][s] = 0;
que.push(node(1, s));
while (!que.empty())
{
node now = que.front(); que.pop();
int u = now.v;
dis[now.x | key[u]][u] = min(dis[now.x | key[u]][u], dis[now.x][u]);
now.x |= key[u];
for (int i = head[u]; ~i; i = nex[i]) {
int v = to[i];
if((now.x&edge[i]) == edge[i]) {
if(dis[now.x][v] > dis[now.x][u] + 1) {
dis[now.x][v] = dis[now.x][u] + 1;
que.push(node(now.x, v));
}
}
}
}
int ans = INF;
for (int i = 1; i < 5005; i++)
ans = min(ans, dis[i][t]);
if(ans == INF) return -1;
return ans;
}
int main()
{
int x1, x2, y1, y2, z, p, k, len = 0;
scanf("%d%d%d%d", &n, &m, &p, &k);
init();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
id[i][j] = ++len;
for (int i = 1; i <= k; i++) {
scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &z);
int u = id[x1][y1], v = id[x2][y2];
mp[u][v] = mp[v][u] = z;
if(z) add(u, v, 1<<z), add(v, u, 1<<z);
}
scanf("%d", &k);
for (int i = 1; i <= k; i++) {
scanf("%d%d%d", &x1, &y1, &z);
key[id[x1][y1]] |= 1<<z;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
int u = id[i][j];
for (k = 0; k < 4; k++) {
int x = i + dx[k], y = j + dy[k];
int v = id[x][y];
if(mp[u][v] == -1 && id[x][y])
add(u, v, 1);
}
}
}
printf("%d\n", spfa());
return 0;
}