思维题最短路

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 EX;

vector e[MAXN + 10], v[MAXN + 10];
long long dis[MAXN + 10];

void dijkstra() {
memset(dis, -1, sizeof(long long) * (n + 3));
priority_queue<pli, vector, greater> pq;
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;
}

posted on 2025-05-12 21:17  下头小美  阅读(13)  评论(0)    收藏  举报