拯救大兵瑞恩
题目
思路
我们可以先状压DP的思维思考,\(dp[x][y][state]\) 表示了再\((x, y)\)位置有钥匙状态为\(state\)的最短距离,考虑到\(key\)最多只有\(10\)种,\(10 \times 10 \times 2^{10}\)≈\(1e6\), 足以开这么大的数组,考虑状态转移:
\[dp[x][y][state] = min(dp[x][y][state], dp[tx][ty][state] + 1) \\
dp[x][y][state | key[x][y]] = min(dp[x][y][state | key[x][y]], dp[x][y][state])
\]
其中\((tx, ty)\)表示了\(x,y\)上下左右四个方向, 当然转移的时候我们还有考虑\(state\)中是否存在该🔑,来判断状态是否可以转移,另外也需要判断是否是门,但是进一步思考,图是存在环形依赖的,我们并不能通过\(dp\) 求得最短路径,所以我们需要像最优贸易,依靠SPFA或者Dijkstra来转移最短路径,由于我们考虑上述转移方程,只存在边权为\(1\)或边权为\(0\)的边,所以可以考虑双端队列广搜来求最短距离,因为依赖于Dijkstra,所以我们最先求到的一定就是最短距离, 部分时候像最优贸易一样, Dijkstra可能不对, 我们就可以用SPFA来解决来解决环形依赖问题, 具体问题具体分析,像这种正常最短路的转移,并不需要思考太多,如果定义从\(0到i\)的一条最小边权,就需要我们手动演算dijkstra是否正确。
下面的代码中我对坐标轴进行了hash,用\(dp[pos][state]\)来表示一个状态。
Code
#include <iostream>
#include <vector>
#include <queue>
#include <deque>
#include <cstring>
#define ff first
#define ss second
using i64 = long long;
#define debug(x) std::cout << x << "\n"
int n, m, p, k, s;
const int N = 20, M = 400, P = 1 << 10 + 1;
int dx[] = {0, -1, 1, 0}, dy[] = {1, 0, 0, -1};
int calc(int x, int y) {
return (x + 1) * 11 + y;
}
int key[N], door[M][M], dist[M][P];
bool st[M][P];
int bfs() {
memset(dist, 0x3f, sizeof dist);
std::deque<std::pair<int, int>> q1;
q1.push_back({calc(1, 1), 0});
dist[calc(1, 1)][0] = 0;
while (q1.size()) {
auto t = q1.front(); q1.pop_front();
if (st[t.ff][t.ss]) continue;
st[t.ff][t.ss] = true;
int x = t.ff / 11 - 1, y = t.ff % 11;
if (x == n && y == m) {
return dist[t.ff][t.ss];
}
if (key[t.ff]) {
int state = t.ss | key[t.ff];
if (dist[t.ff][state] > dist[t.ff][t.ss]) {
dist[t.ff][state] = dist[t.ff][t.ss];
q1.push_front({t.ff, state});
}
}
for (int i = 0; i <= 3; i ++) {
int tx = x + dx[i], ty = y + dy[i];
if (tx < 1 || tx > n || ty < 1 || ty > m) continue;
int k = calc(tx, ty);
if (door[t.ff][k] == -1) continue;
if (door[t.ff][k] && !(t.ss >> door[t.ff][k] - 1 & 1)) {
continue;
}
if (dist[k][t.ss] > dist[t.ff][t.ss] + 1) {
dist[k][t.ss] = dist[t.ff][t.ss] + 1;
q1.push_back({k, t.ss});
}
}
}
return -1;
}
int main() {
std::cin >> n >> m >> p >> k;
for (int i = 1; i <= k; i ++) {
int x1, y1, x2, y2, c;
std::cin >> x1 >> y1 >> x2 >> y2 >> c;
if (!c) c = -1;
int p1 = calc(x1, y1), p2 = calc(x2, y2);
door[p1][p2] = door[p2][p1] = c;
}
std::cin >> s;
for (int i = 1; i <= s; i ++) {
int x, y, q;
std::cin >> x >> y >> q;
key[calc(x, y)] |= 1 << q - 1;
}
std::cout << bfs();
}