【网络流24题】孤岛营救问题(分层图BFS)
题目描述
\(1944\) 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为 \(N\) 行,东西方向被划分为 \(M\) 列,于是整个迷宫被划分为 \(N\times M\) 个单元。每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示。南北或东西方向相邻的 \(2\) 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成\(P\) 类,打开同一类的门的钥匙相同,不同类门的钥匙不同。
大兵瑞恩被关押在迷宫的东南角,即 \((N,M)\) 单元里,并已经昏迷。迷宫只有一个入口,在西北角。也就是说,麦克可以直接进入 \((1,1)\) 单元。另外,麦克从一个单元移动到另一个相邻单元的时间为 \(1\),拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。
试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。
输入格式
第 \(1\) 行有 \(3\) 个整数,分别表示 \(N,M,P\) 的值。
第 \(2\) 行是 \(1\) 个整数 \(K\),表示迷宫中门和墙的总数。
第 \(I+2\) 行\((1\leq I\leq K)\),有 \(5\) 个整数,依次为\(X_{i1},Y_{i1},X_{i2},Y_{i2},G_i\):
-
当 \(G_i \geq 1\) 时,表示 \((X_{i1},Y_{i1})\) 单元与 \((X_{i2},Y_{i2})\) 单元之间有一扇第 \(G_i\) 类的门
-
当 \(G_i=0\) 时,表示 \((X_{i1},Y_{i1})\) 单元与 \((X_{i2},Y_{i2})\) 单元之间有一堵不可逾越的墙(其中,\(|X_{i1}-X_{i2}|+|Y_{i1}-Y_{i2}|=1\),\(0\leq G_i\leq P\))。
第 \(K+3\) 行是一个整数 \(S\),表示迷宫中存放的钥匙总数。
第 \(K+3+J\) 行\((1\leq J\leq S)\),有 \(3\) 个整数,依次为 \(X_{i1},Y_{i1},Q_i\):表示第 \(J\) 把钥匙存放在 \((X_{i1},Y_{i1})\)单元里,并且第 \(J\) 把钥匙是用来开启第 \(Q_i\) 类门的。(其中\(1\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
提示
\(|X_{i1}-X_{i2}|+|Y_{i1}-Y_{i2}|=1,0\leq G_i\leq P\)
\(1\leq Q_i\leq P\)
\(N,M,P\leq10, K<150,S\leq 14\)
题解
事实上本题与网络流无关。但是建图方式对解决这类问题很有帮助。
考虑将当前坐标+当前拥有的钥匙集合定义为一个状态,则总的状态数为\(O(NM \cdot2^P)\)
这样,可以移动的相邻位置之间连边权为1的双向边,相同位置、增添一把钥匙连边权为0的边。在该图上进行BFS即可。
//
// Created by blackbird on 2023/8/30.
//
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define x first
#define y second
using namespace std;
const int N = 2e6 + 10, inf = 1e10;
struct node {
int x, y, st;
} a[N];
int n, m, p;
inline int getid(int x, int y, int st) {
return st * n * m + (x - 1) * m + y;
}
inline int ceil(int x, int y) {
return x / y + (x % y != 0);
}
string printbit(int x) {
string res;
for (int i = 0; i < p; i++) {
res.push_back('0' + (x >> i & 1));
}
return res;
}
void print(pii u) {
int id = u.first;
int st = ceil(id, n * m) - 1;
int xy = id % (n * m) == 0 ? n * m : id % (n * m);
int x = ceil(xy, m);
int y = xy - (x - 1) * m;
cout << x << " " << y << " " << printbit(st) << " " << u.second << "\n";
}
bool ed[N];
vector<pii> g[N];
vector<int> key[21][21];
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
void solve() {
cin >> n >> m >> p;
for (int st = 0; st <= (1 << p) - 1; st++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
int id = getid(i, j, st);
a[id] = {i, j, st};
if (i == n && j == m) {
ed[id] = true;
}
}
}
}
int tot; cin >> tot;
set<pair<pii,pii>> vised;
for (int i = 1; i <= tot; i++) {
int x1, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
c--;
vised.insert({{x1, y1}, {x2, y2}});
vised.insert({{x2, y2}, {x1, y1}});
for (int st = 0; st <= (1 << p) - 1; st++) {
int s = getid(x1, y1, st), t = getid(x2, y2, st);
if (c < 0) continue;
if (st >> c & 1) {
g[s].emplace_back(t, 1);
g[t].emplace_back(s, 1);
}
}
}
for (int st = 0; st <= (1 << p) - 1; st++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 0; k < 4; k++) {
int x = i, y = j, tx = x + dx[k], ty = y + dy[k];
if (tx < 1 || tx > n || ty < 1 || ty > m) continue ;
int s = getid(x, y, st), t = getid(tx, ty, st);
if (!vised.count({{x, y}, {tx, ty}})) {
g[s].emplace_back(t, 1);
g[t].emplace_back(s, 1);
//print({s, 0}); print({t, 0});
}
}
}
}
}
cin >> tot;
for (int i = 1; i <= tot; i++) {
int x, y, c;
cin >> x >> y >> c;
c--;
for (int st1 = 0; st1 <= (1 << p) - 1; st1++) {
if (!(st1 >> c & 1)) {
int st2 = st1 | (1 << c);
int s = getid(x, y, st1), t = getid(x, y, st2);
// print({s, 0}); print({t, 0});
// cout << "__\n";
g[s].emplace_back(t, 0);
g[t].emplace_back(s, 0);
}
}
}
queue<pii> q; q.push({getid(1, 1, 0), 0});
vector<bool> vis(N); vis[getid(1, 1, 0)] = true;
int ans = inf;
while (!q.empty()) {
pii u = q.front(); q.pop(); //print(u);
if (ed[u.first]) {
ans = min(ans, u.second);
continue;
}
for (auto &[v, w] : g[u.first]) {
if (vis[v]) continue;
q.push({v, u.second + w});
vis[v] = true;
}
}
if (ans == inf) ans = -1;
cout << ans << "\n";
}
signed main() {
//cin.tie(nullptr)->sync_with_stdio(false); srand(time(0));
int T = 1; //cin >> T;
while (T--) {
solve();
}
return 0;
}