Hướng dẫn giải của [2627 KS3_DE2] Câu 3: Loa cảnh báo lũ


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 độ dài D nhỏ nhất để k loa, mỗi loa phủ một đoạn [y, y + D], phủ được mọi hộ dân.

Nhận xét: nếu đặt được với D thì với mọi D' \ge D cũng đặt được, nên có thể tìm D bằng tìm kiếm nhị phân. Với một D cố định, kiểm tra bằng tham lam: sắp xếp các vị trí, đặt loa đầu tiên tại hộ chưa được phủ đầu tiên, phủ đến x + D, rồi lặp lại. Nếu cần không quá k loa thì D khả thi.

Sub 1: Thử lần lượt — ~O(D_{max} \cdot n)~, dùng khi ~n \le 15~ và ~x \le 100~

Thử D = 0, 1, 2, ... cho đến khi kiểm tra tham lam thành công. Vì x \le 100 nên D không quá 100. Nếu chỉ nộp sub này: đúng 13/20 test.

Sub 2: Tìm kiếm nhị phân trên D — ~O(n \log X)~

Tìm kiếm nhị phân D trên [0, xmax - xmin], mỗi bước kiểm tra tham lam trong O(n). Với tổng n \le 5 \cdot 10^5 thì chạy kịp. Bẫy: x có thể trùng nhau và không được sắp sẵn, nên phải sắp xếp trước. Kết quả dùng long long cho an toàn. Nếu chỉ nộp sub này: đúng 20/20 test.

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

Hàm main đọc t xã. Với mỗi xã, nếu n \le 15 và x \le 100 gọi sub1, ngược lại gọi sub2.

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

int n, k;
vector<long long> x;   // vi tri cac ho, da sap xep tang dan

// Kiem tra voi do dai D co the phu moi ho bang toi da k loa khong.
// Tham lam: loa dau tien dat tai vi tri ho chua duoc phu dau tien, phu den x + D; lap lai.
bool ok(long long D) {
    int cnt = 0, i = 0;
    while (i < n) {
        cnt++;
        if (cnt > k) return false;
        long long end = x[i] + D;
        while (i < n && x[i] <= end) i++;
    }
    return true;
}

// Sub 1: thu lan luot D = 0, 1, 2, ... den khi ok(D). Dung khi n <= 15 va x <= 100 (D <= 100)
void sub1() {
    for (long long D = 0;; D++)
        if (ok(D)) { cout << D << "\n"; return; }
}

// Sub 2 (day du): tim kiem nhi phan tren D trong [0, x_max - x_min]. ok(D) don dieu theo D.
// O(n log(max x)) moi truy van.
void sub2() {
    long long lo = 0, hi = x[n - 1] - x[0];
    while (lo < hi) {
        long long mid = (lo + hi) / 2;
        if (ok(mid)) hi = mid; else lo = mid + 1;
    }
    cout << lo << "\n";
}

int main() {
    freopen("CAU3.INP", "r", stdin);
    freopen("CAU3.OUT", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        cin >> n >> k;
        x.assign(n, 0);
        for (int i = 0; i < n; i++) cin >> x[i];
        sort(x.begin(), x.end());
        if (n <= 15 && x[n - 1] <= 100) 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.