Hướng dẫn giải của [2627 KS3_DE2] Câu 3: Loa cảnh báo lũ
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