Hướng giải của Đo dây cho đèn đường
Nhớ rằng hướng dẫn giải này chỉ nên sử dụng khi bế tắc, và tuyệt đối không nên sao chép mã nguồn kèm theo. Hãy tôn trọng tác giả bài tập và người viết hướng dẫn giải.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.
Lời giải: Đo dây cho đèn đường
Ý tưởng
Sắp xếp vị trí các ngôi làng tăng dần. Với bán kính \(R\), ta tham lam đặt cột đèn đầu tiên tại \(p_1\), nó phủ \([p_1 - R, p_1 + R]\). Cột này phủ hết các điểm liên tiếp \(p_i \le p_1 + 2R\). Điểm đầu tiên \(p_j > p_1 + 2R\) cần cột mới đặt tại \(p_j\). Tiếp tục.
Hàm \(f(R) = $ "số cột cần dùng" là hàm đơn điệu giảm theo $R\).
Thuật toán
- Cận dưới: \(0\).
- Cận trên: \(L\) (bán kính \(L\) chắc chắn đủ dù chỉ \(1\) cột).
- Thử \(R\): nếu số cột \(\le K\) thì \(hi = R\). Ngược lại \(lo = R + 1\).
Độ phức tạp
- Thời gian: \(O(N \log L)\).
- Bộ nhớ: \(O(N)\).
Cạm bẫy
- Vị trí cột đặt tại điểm bắt đầu \(p_i\), phủ \(p_i + 2R\) (đi xa về bên phải).
Mã nguồn tham khảo
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N, K;
long long L;
cin >> N >> K >> L;
vector<long long> p(N);
for (int i = 0; i < N; i++) cin >> p[i];
sort(p.begin(), p.end());
auto can = [&](long long R) -> bool {
size_t used = 0, i = 0;
while (i < (size_t)N && used < (size_t)K) {
++used;
long long right = p[i] + 2 * R;
while (i < (size_t)N && p[i] <= right) i++;
}
return used <= (size_t)K && i == (size_t)N;
};
long long lo = 0, hi = L;
while (lo < hi) {
long long mid = lo + (hi - lo) / 2;
if (can(mid)) hi = mid;
else lo = mid + 1;
}
cout << lo << "\n";
return 0;
}
Nhận xét