思维题最短路
K - Escape Plan
2019陕西省省赛
题解
维护 dis[x] 表示最差情况下从点
走到任意终点的最短路,显然 dis[终点] = 0,其它的 dis 值考虑从终点倒推回来。
考虑从终点开始 Dijkstra 的过程。
当我们从 Dijkstra 的堆顶第一次取出节点
时(假设对应的是边
),说明从
出发,走
这条边是去终点的最近道路。为了让答案尽可能差,我们必须得把这条边堵上。
同样地,当我们从 Dijkstra 的堆顶第二次取出节点
时(假设对应的是边
),说明从
出发,走
这条边是去终点的第二近道路。为了让答案尽可能差,我们也得把这条边堵上。
...
当我们从 Dijkstra 的堆顶第
次取出节点
时,因为我们的堵塞次数已经用完了,那么 dis[v] 的值就确定为本次取出的距离。节点
继续向相邻节点转移。
最后 dis[1] 就是答案。复杂度
。
参考代码
include <bits/stdc++.h>
define MAXN ((int) 1e5)
using namespace std;
typedef pair<long long, int> pli;
int n, m, D[MAXN + 10];
vector
vector
long long dis[MAXN + 10];
void dijkstra() {
memset(dis, -1, sizeof(long long) * (n + 3));
priority_queue<pli, vector
for (int x : EX) pq.push(pli(0, x));
while (!pq.empty()) {
pli p = pq.top(); pq.pop();
int sn = p.second;
if (dis[sn] >= 0) continue;
// 只有堵塞次数不够了,才能够更新 dis[sn]
if (--D[sn] >= 0) continue;
dis[sn] = p.first;
for (int i = 0; i < e[sn].size(); i++) {
int fn = e[sn][i];
if (dis[fn] >= 0) continue;
pq.push(pli(dis[sn] + v[sn][i], fn));
}
}
}
void solve() {
int K; scanf("%d%d%d", &n, &m, &K);
EX.clear();
while (K--) {
int x; scanf("%d", &x);
EX.push_back(x);
}
for (int i = 1; i <= n; i++) scanf("%d", &D[i]);
// 把终点的堵塞次数都改成 0,防止 dijkstra 无法开始
for (int x : EX) D[x] = 0;
for (int i = 1; i <= n; i++) e[i].clear(), v[i].clear();
for (int i = 1; i <= m; i++) {
int x, y, z; scanf("%d%d%d", &x, &y, &z);
e[x].push_back(y); v[x].push_back(z);
e[y].push_back(x); v[y].push_back(z);
}
dijkstra();
printf("%lld\n", dis[1]);
}
int main() {
int tcase; scanf("%d", &tcase);
while (tcase--) solve();
return 0;
}
浙公网安备 33010602011771号