gyro永不抽风

ああああああああああああああああおおおおおおおおおおおおおおおお

P4011 孤岛营救问题 - 分层图最短路

题目

题目描述

19441944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为 NN 行,东西方向被划分为 MM 列,于是整个迷宫被划分为 N×MN\times M 个单元。每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示。南北或东西方向相邻的 22 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成PP 类,打开同一类的门的钥匙相同,不同类门的钥匙不同。

大兵瑞恩被关押在迷宫的东南角,即 (N,M)(N,M) 单元里,并已经昏迷。迷宫只有一个入口,在西北角。也就是说,麦克可以直接进入 (1,1)(1,1) 单元。另外,麦克从一个单元移动到另一个相邻单元的时间为 11,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。

试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。

输入格式

11 行有 33 个整数,分别表示 N,M,PN,M,P 的值。

22 行是 11 个整数 KK,表示迷宫中门和墙的总数。

I+2I+2(1IK)(1\leq I\leq K),有 55 个整数,依次为Xi1,Yi1,Xi2,Yi2,GiX_{i1},Y_{i1},X_{i2},Y_{i2},G_i

  • Gi1G_i \geq 1 时,表示 (Xi1,Yi1)(X_{i1},Y_{i1}) 单元与 (Xi2,Yi2)(X_{i2},Y_{i2}) 单元之间有一扇第 GiG_i 类的门

  • Gi=0G_i=0 时,表示 (Xi1,Yi1)(X_{i1},Y_{i1}) 单元与 (Xi2,Yi2)(X_{i2},Y_{i2}) 单元之间有一堵不可逾越的墙(其中,Xi1Xi2+Yi1Yi2=1|X_{i1}-X_{i2}|+|Y_{i1}-Y_{i2}|=10GiP0\leq G_i\leq P)。

K+3K+3 行是一个整数 SS,表示迷宫中存放的钥匙总数。

K+3+JK+3+J(1JS)(1\leq J\leq S),有 33 个整数,依次为 Xi1,Yi1,QiX_{i1},Y_{i1},Q_i:表示第 JJ 把钥匙存放在 (Xi1,Yi1)(X_{i1},Y_{i1})单元里,并且第 JJ 把钥匙是用来开启第 QiQ_i 类门的。(其中1QiP1\leq Q_i\leq P)。

输入数据中同一行各相邻整数之间用一个空格分隔。

输出格式

将麦克营救到大兵瑞恩的最短时间的值输出。如果问题无解,则输出 1-1

输入输出样例

输入 #1
4 4 9
9
1 2 1 3 2
1 2 2 2 0
2 1 2 2 0
2 1 3 1 0
2 3 3 3 0
2 4 3 4 1
3 2 3 3 0
3 3 4 3 0
4 3 4 4 0
2
2 1 2
4 2 1
输出 #1
14

说明/提示

Xi1Xi2+Yi1Yi2=1,0GiP|X_{i1}-X_{i2}|+|Y_{i1}-Y_{i2}|=1,0\leq G_i\leq P

1QiP1\leq Q_i\leq P

N,M,P10,K<150,S14N,M,P\leq10, K<150,S\leq 14

题解

题目中问要花费的最短时间,那其实就可以联想到最短路问题或者直接BFS。那记下来的问题就在于如何建图。我们可以发现,这里有着$p$把钥匙,要处理好这些钥匙是问题的关键。所以,我们可以建立分层图,而且分层图的层数是当前所有的钥匙的状态压缩。

形象地解释的话:如果现在是$i$层,那么我们先将$i$转换为二进制(假设一共有4种钥匙种类,那么$i$可能是:

意思就是有着第一种、第三种、第四种钥匙的层(从后往前看)。如果无法理解分层图,那么不妨看看前两篇博文,都是分层图的题。

所以接下来,我们只需要关心如何建立图,图建立好之后我们就可以扔给堆优化过的Dijkstra直接算就行了。

我们根据以下几点建图:

  • 如果有不可逾越的墙,不连边
  • 如果输入中没有提到这条边,在每层都连
  • 如果输入中提到了,且需要钥匙,那么先循环,然后按位运算匹配当前层是否拥有开门钥匙。若有则连边
  • 如果这一点有钥匙,那么在这个点,从所有没有钥匙的层,可以转移到有钥匙的层,连边。

最后答案其实就是把每一层的$(N, M)$遍历一遍去一个最小。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <iostream>
#include <queue>
#include <cstdio>

using namespace std;

const int maxg = 10;
const int maxk = 155;
const int maxs = 15;
const int maxn = 11;
const int maxm = 11;
int n, m, p, k, s;
int ecnt = 2;
int head[maxn * maxm * (1 << maxg)], dis[maxn * maxm * (1 << maxg)];
bool vis[maxn * maxm * (1 << maxg)];
struct edge { int to, next, w; } g[(1 << maxg) * (maxk * 2 * 4 + maxs)];
void add_edge(int u, int v, int w) { g[ecnt] = (edge) { v, head[u], w }; head[u] = ecnt ++; }
void add_edge(int x1, int y1, int l1, int x2, int y2, int l2, int w) {
add_edge(y1 + (x1 - 1) * m + m * n * l1,
y2 + (x2 - 1) * m + m * n * l2, w);
}
inline int id (int x, int y) {
return (x - 1) * m + y ;
}
int map[200][200];

struct node {
int dis, pos;
friend bool operator < (const node &a, const node &b) { return a.dis > b.dis; }
};
priority_queue<node> q;

void dijkstra() {
for (int i = 1; i <= m * n * (1 << p); i ++) dis[i] = 2e9;
dis[1] = 0;
q.push((node) { dis[1], 1 });
while (!q.empty()) {
int u = q.top().pos; q.pop();
if (vis[u]) continue;
vis[u] = true;
for (int e = head[u]; e; e = g[e].next) {
int v = g[e].to;
if (dis[v] > dis[u] + g[e].w) {
dis[v] = dis[u] + g[e].w;
q.push((node) { dis[v], v });
}
}
}
}

int main() {
cin >> n >> m >> p >> k;
for (int i = 1; i <= k; i ++) {
int x1, x2, y1, y2, g;
cin >> x1 >> y1 >> x2 >> y2 >> g;
if (g == 0) g = -1;
map[id(x1, y1)][id(x2, y2)] = g;
map[id(x2, y2)][id(x1, y1)] = g;
}
for (int l = 0; l < (1 << p); l ++) {
for (int i = 1; i <= n; i ++) { // x
for (int j = 1; j <= m; j ++) { // y
// right
if (j < m) {
int g = map[id(i, j)][id(i, j + 1)];
if (g == 0) {
add_edge(i, j, l, i, j + 1, l, 1);
add_edge(i, j + 1, l, i, j, l, 1);
}
if ((l & (1 << (g - 1))) && (g != 0) && (g != -1)) {
add_edge(i, j, l, i, j + 1, l, 1);
add_edge(i, j + 1, l, i, j, l, 1);
}
}
// down
if (i < n) {
int g = map[id(i, j)][id(i + 1, j)];
if (g == 0) {
add_edge(i, j, l, i + 1, j, l, 1);
add_edge(i + 1, j, l, i, j, l, 1);
}
if ((l & (1 << (g - 1))) && (g != 0) && (g != -1)) {
add_edge(i, j, l, i + 1, j, l, 1);
add_edge(i + 1, j, l, i, j, l, 1);
}
}
}
}
}
cin >> s;
for (int i = 1; i <= s; i ++) {
int x, y, q; cin >> x >> y >> q;
for (int l = 0; l < (1 << p); l ++) {
if (l & (1 << (q - 1))) continue;
add_edge(x, y, l, x, y, (l | (1 << (q - 1))), 0);
}
}
dijkstra();
int ans = 2e9;
for (int i = 1; i <= (1 << p); i ++) ans = min(ans, dis[m * n * i]);
if (ans == 2e9) ans = -1;
cout << ans << endl;
return 0;
}

感谢

感谢@nwt(在右边的友链里 wennitao),在debug的时候提供了莫大的帮助。(qhy是屑)

__EOF__
-------------本文结束感谢您的阅读-------------

本文标题:P4011 孤岛营救问题 - 分层图最短路

文章作者:gyro永不抽风

发布时间:2020年09月19日 - 19:09

最后更新:2020年09月19日 - 19:09

原始链接:http://gyrojeff.moe/2020/09/19/P4011-%E5%AD%A4%E5%B2%9B%E8%90%A5%E6%95%91%E9%97%AE%E9%A2%98-%E5%88%86%E5%B1%82%E5%9B%BE%E6%9C%80%E7%9F%AD%E8%B7%AF/

许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 转载请保留原文链接及作者!

真的不买杯奶茶吗?T^T

欢迎关注我的其它发布渠道