湖南省賽
K題
題意
你可以免費移動經過一條邊,求在滿足在任意點開始都能成功渡劫的最小花費。
思路
- 建一個虛擬源點,連向每一個點,將這條邊的邊權設為這個點渡劫需要的花費。
- 跑最短路,這樣會把每一種情況囊括在內,但是沒有考慮免費的移動。
- 建一個
dist2
陣列,用來記錄每一個點當前真正的最小花費(已用免費次數),類似狀態轉移。 - 每一次對
dist1[u]
,dist2[v] + w
和本身取min
進行更新。 - 最後遍歷
dist2
陣列取max
即為答案。
程式碼
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
#define int long long
int n, m;
cin >> n >> m;
vector<vector<pair<int, int>>> g(n + 1);
int INF = 2e18;
vector<int> dist1(n + 1, INF), dist2(n + 1, INF);
while (m--) {
int a, b, c;
cin >> a >> b >> c;
g[a].emplace_back(b, c);
g[b].emplace_back(a, c);
}
vector<int> a(n + 1);
int minn = INF;
int st = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
dist2[i] = a[i]; // 初始化
}
for (int i = 1; i <= n; i++) {
g[0].emplace_back(i, a[i]);
}
vector<int> f(n + 1);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> heap;
dist1[st] = 0;
dist2[st] = 0; // dist1 記錄不用,dist2 記錄用免費的,也就是當前最小
int ans = 0; // 要取 max
heap.emplace(dist1[st], st);
while (heap.size()) {
auto [d, u] = heap.top();
heap.pop();
if (f[u]) continue;
f[u] = 1;
for (auto [v, w] : g[u]) {
if (dist1[v] > dist1[u] + w) { // 算出到上一個的消耗
f[u] = 0;
dist1[v] = min({dist1[u] + w, dist1[v]});
heap.emplace(dist1[v], v);
}
if (u) dist2[v] = min({dist1[u], dist2[u] + w, dist2[v]}); // 更新每個點的最小能量
}
}
for (int i = 1; i <= n; i++) {
ans = max(ans, dist2[i]);
}
cout << ans << '\n';
}