Hướng dẫn giải của [2526 KS CHV] Câu 4: Hệ thống cấp nước


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Hướng dẫn giải

Cần tìm đường từ khu 1 đến khu K sao cho ống nhỏ nhất trên đường là lớn nhất. Đây là bài tìm đường đi có "độ hẹp" lớn nhất.

Sub 1: Thử từng ngưỡng với BFS — ~O(M \cdot (N + M))~, dùng khi ~M \le 500~

Với mỗi đường kính d xuất hiện trong đề, chỉ giữ các ống có đường kính lớn hơn hoặc bằng d, rồi dùng BFS kiểm tra khu 1 và khu K có thông nhau không. Lấy d lớn nhất còn thông. Nếu không có đường nào thì in -1. Sub này đúng 15/20 test (5 test còn lại có M lớn nên quá thời gian, nằm ngoài miền của sub).

Sub 2: Kruskal giảm dần với DSU — ~O(M \log M)~

Sắp xếp ống theo đường kính giảm dần, lần lượt nối hai đầu ống. Khi khu 1 và khu K vừa được nối thông thì đường kính của ống vừa thêm chính là đáp án. Sub này đúng 20/20 test.

Chương trình hoàn chỉnh

Hàm main đọc N, M, K và các ống. Nếu ~M \le 500~ gọi sub 1, còn lại gọi sub 2.

#include <bits/stdc++.h>
using namespace std;

int n, m, K;
vector<array<int, 3>> E;   // E[i] = {u, v, d}

// Sub 1: thử từng giá trị X (là đường kính của một ống), với mỗi X kiểm tra bằng BFS
// xem còn đường từ khu 1 đến khu K chỉ dùng ống có d >= X. Lấy X lớn nhất còn đi được.
// Độ phức tạp O(M * (N + M)). Dùng khi M <= 500.
void sub1() {
    vector<int> cand;
    for (auto &e : E) cand.push_back(e[2]);
    sort(cand.begin(), cand.end());
    cand.erase(unique(cand.begin(), cand.end()), cand.end());

    int best = -1;
    for (int X : cand) {
        vector<vector<int>> g(n + 1);
        for (auto &e : E)
            if (e[2] >= X) {
                g[e[0]].push_back(e[1]);
                g[e[1]].push_back(e[0]);
            }
        vector<char> vis(n + 1, 0);
        queue<int> q;
        q.push(1);
        vis[1] = 1;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int w : g[u])
                if (!vis[w]) {
                    vis[w] = 1;
                    q.push(w);
                }
        }
        if (vis[K]) best = max(best, X);
    }
    cout << best << "\n";
}

// Sub 2: Kruskal giảm dần, dùng DSU, O(M log M).
// Thêm các ống theo đường kính giảm dần; ống vừa làm 1 và K nối thông thì đó là đáp án.
int par[5005];
int findp(int x) { return par[x] == x ? x : par[x] = findp(par[x]); }

void sub2() {
    sort(E.begin(), E.end(), [](const array<int, 3> &a, const array<int, 3> &b) { return a[2] > b[2]; });
    for (int i = 1; i <= n; i++) par[i] = i;
    for (auto &e : E) {
        int a = findp(e[0]), b = findp(e[1]);
        if (a != b) par[a] = b;
        if (findp(1) == findp(K)) {
            cout << e[2] << "\n";
            return;
        }
    }
    cout << -1 << "\n";
}

int main() {
    freopen("CAU4.INP", "r", stdin);
    freopen("CAU4.OUT", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> m >> K;
    E.resize(m);
    for (int i = 0; i < m; i++) cin >> E[i][0] >> E[i][1] >> E[i][2];
    if (m <= 500) sub1();
    else sub2();
    return 0;
}

Bình luận

Hãy đọc nội quy trước khi bình luận.


Không có bình luận tại thời điểm này.