2024湖南省賽題解(不全)

知离叶發表於2024-10-30

湖南省賽


K題

題意

你可以免費移動經過一條邊,求在滿足在任意點開始都能成功渡劫的最小花費。


思路

  1. 建一個虛擬源點,連向每一個點,將這條邊的邊權設為這個點渡劫需要的花費。
  2. 跑最短路,這樣會把每一種情況囊括在內,但是沒有考慮免費的移動。
  3. 建一個 dist2 陣列,用來記錄每一個點當前真正的最小花費(已用免費次數),類似狀態轉移。
  4. 每一次對 dist1[u]dist2[v] + w 和本身取 min 進行更新。
  5. 最後遍歷 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';
}

相關文章