多层图 T3 #6121. 「网络流 24 题」孤岛营救 - 题目 - LibreOJ (loj.ac)
题目描述
1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为 行,东西方向被划分为 列, 于是整个迷宫被划分为 个单元。每一个单元的位置可用一个有序数对 (单元的行号, 单元的列号) 来表示。南北或东西方向相邻的 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成 类, 打开同一类的门的钥匙相同,不同类门的钥匙不同。
大兵瑞恩被关押在迷宫的东南角,即 单元里,并已经昏迷。迷宫只有一个入口, 在西北角。也就是说,麦克可以直接进入 单元。另外,麦克从一个单元移动到另一个 相邻单元的时间为 ,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。
试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。
输入格式
第一行有三个整数,分别表示 的值。
第二行是一个整数,表示迷宫中门和墙的总数。
第 行 ,有 个整数,依次为 :当 时,表示 单元与 单元之间有一扇第 类的门,当 时, 表示 单元与 单元之间有一堵不可逾越的墙。
第 行是一个整数 ,表示迷宫中存放的钥匙总数。
第 行 ,有 个整数,依次为 ,表示第 把钥匙存放在 单元里,并且第 把钥匙是用来开启第 类门。
输入数据中同一行各相邻整数之间用一个空格分隔。
输出格式
输出麦克营救到大兵瑞恩的最短时间。如果问题无解,则输出 。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
样例
输入
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
输出
14
数据范围与提示
思路
如果还按照最短路中 走到第i个点的最短路径来考虑, 是没法知道当前是否有钥匙的。可以增加一维, 位置相同但此时拥有钥匙数量不同的情况算作不同的点, 最后求一遍最短路即可。 增加点后可以发现, 如果在当前点不动, 只捡钥匙的话, 不会耗时, 故该边权值为0, 其余走一格后的边权值为1, 既然是01图, 就可以用BFS双端队列来求解。
钥匙数就用二进制状态压缩。
代码
#define S second
#define F first
#define pb push_back
#define pf push_front
#define mem(x, n) memset(x, n, sizeof x)
#include <iostream>
#include <cstring>
#include <set>
#include <deque>
using namespace std;
typedef pair<int, int> PII;
// #define debug
typedef long long LL;
// using i64 = long long;
const int N = 110, M = 3e4;
int h[N], e[M], w[M], ne[M], idx;
int n, m, p;
int g[11][11];
int key[N];
int dist[N][1 << 10];
bool st[N][1 << 10];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int bfs()
{
deque<PII> q;
mem(dist, 0x3f);
q.push_front({g[1][1], 0});
dist[1][0] = 0;
while (q.size())
{
auto t = q.front();
q.pop_front();
int ver = t.F, keys = t.S;
if (st[ver][keys])
continue;
st[ver][keys] = true;
if (ver == g[n][m])
return dist[ver][keys];
if (key[ver])
{
int state = keys | key[ver];
if (dist[ver][state] > dist[ver][keys])
{
dist[ver][state] = dist[ver][keys];
q.push_front({ver, state});
}
}
for (int i = h[ver]; ~i; i = ne[i])
{
int j = e[i];
if (w[i] && !((keys >> (w[i] - 1)) & 1))
continue;
if (dist[j][keys] > dist[ver][keys] + 1)
{
dist[j][keys] = dist[ver][keys] + 1;
q.push_back({j, keys});
}
}
}
return -1;
}
set<PII> Set;
void build()
{
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
for (int k = 0; k < 4; k++)
{
int a = i + dx[k], b = j + dy[k];
if (a < 1 || a > n || b < 1 || b > m)
continue;
if (Set.count({g[i][j], g[a][b]}))
continue;
add(g[i][j], g[a][b], 0);
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
while (cin >> n >> m >> p)
{
for (int i = 1, k = 1; i <= n; i++)
for (int j = 1; j <= m; j++, k++)
g[i][j] = k;
mem(h, -1);
int k;
cin >> k;
while (k--)
{
int x1, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
int a = g[x1][y1], b = g[x2][y2];
Set.insert({a, b});
Set.insert({b, a});
if (c)
add(a, b, c), add(b, a, c);
}
build();
int s;
cin >> s;
while (s--)
{
int a, b, c;
cin >> a >> b >> c;
a = g[a][b];
key[a] |= 1 << (c - 1);
}
cout << bfs() << endl;
}
return 0;
}