题解:P1811 最短路
YujinSharp · · 题解
P1811 最短路
水一篇题解。
题目分析
这道题看是一个简单的无向图最短路问题添加了一点限制条件,即给定
这说明着路径是否合法不仅和当前点有关,还和上一个点有关。针对简单的边权为 dist[u] 记录最短路,而针对这题,需要把状态扩展为 dist[u][prev],表示从
思路实现
-
状态扩展
相较于普通的边权为1 的最短路算法,将普通的每个点的状态记录增加了对之前点的记录,相当于记录了prev 到u 的有向边的状态。 -
多个
C 的存储
对于每个(A,B) ,可能会有多个禁止的C 。 因此使用id_map[A][B]给每个二元组分配一个编号,对应到ban_list中的集合,存储所有禁止的C 。
如此一来,就能实现\mathcal{O}(1) 地查询限制。 -
BFS的状态转移
从u 可以尝试走到v 。
如果存在(prev, u, v) ,就跳过。
否则更新dist[v][u] = dist[u][prev] + 1,并记录前驱,用于还原路径。 -
路径还原
当BFS找到终点N 时,输出dist[N][prev]。
然后沿着fa[N][N]数组回溯路径,最后逆序输出。
代码实现
下面的代码使用了二维数组 dist[N][N] 和 fa[N][N] 来存状态,使用 id_map 和 ban_list 存储限制条件。
复杂度为
#include <bits/stdc++.h>
using namespace std;
const int N = 3010;
int dist[N][N]; // dist[u][prev]
pair<int, int> fa[N][N]; // 记录前驱
vector<int> graph[N]; // 邻接表
int id_map[N][N]; // (a,b) -> 下标
vector<unordered_set<int>> ban_list; // 对应的禁止集合
int main() {
int n, m, k;
cin >> n >> m >> k;
// 建图
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
graph[a].push_back(b);
graph[b].push_back(a);
}
// 初始化 id_map
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
id_map[i][j] = -1;
// 读入限制条件
for (int i = 0; i < k; i++) {
int a, b, c;
cin >> a >> b >> c;
if (id_map[a][b] == -1) {
id_map[a][b] = ban_list.size();
ban_list.push_back(unordered_set<int>());
}
ban_list[id_map[a][b]].insert(c);
}
// 初始化 dist
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
dist[i][j] = -1;
// BFS
queue<pair<int, int>> q;
dist[1][0] = 0;
fa[1][0] = {0, 0};
q.push({1, 0});
while (!q.empty()) {
int u = q.front().first;
int prev = q.front().second;
q.pop();
// 到达终点
if (u == n) {
cout << dist[u][prev] << endl;
// 路径回溯
vector<int> path;
int cur_u = u, cur_prev = prev;
path.push_back(cur_u);
while (cur_u != 1) {
auto p = fa[cur_u][cur_prev];
cur_u = p.first;
cur_prev = p.second;
path.push_back(cur_u);
}
reverse(path.begin(), path.end());
for (size_t i = 0; i < path.size(); i++) {
cout << path[i] << (i + 1 == path.size() ? '\n' : ' ');
}
return 0;
}
// 扩展邻居
for (int v : graph[u]) {
int id = id_map[prev][u];
if (id != -1 && ban_list[id].count(v)) {
continue; // 被禁止
}
if (dist[v][u] == -1) {
dist[v][u] = dist[u][prev] + 1;
fa[v][u] = {u, prev};
q.push({v, u});
}
}
}
// 无解
cout << -1 << "\n";
return 0;
}
完结撒花。