Problem C: 簡單的數值統計
阿新 • • 發佈:2020-12-18
答案具有單調性,即存在一個最優解(最短條約距離的最大值),使得大於此解值的值都需要移走多於M個石頭,而且所有小於此解值的所有值都是可行解(但他們不是最優的,所以不是答案).
可以先假定一個值,檢測他是否為可行解,並且通過多次這樣的假定得出最優解.
使用函式bool C(x),若解x可行則返回true,否則返回false.
根據答案的單調性,接下來通過二分來假定答案就可以求解.二分的寫法:
int l = 0, r = 1000000000; while (l < r) { int mid = l + r + 1 >> 1; if (C(mid)) l= mid; else r = mid - 1; }
此時l(l == r)即為最優解.
注意到,這裡l = mid; r = mid - 1;是根據實際問題選擇的,在這裡含義是如果mid值可行,那麼在mid及其右側可能有比mid更優的解(當然,首先得有可行解,此時可行即比mid更優).
如果mid值不可行,顯然mid及其右側均無可行解,不需要考慮這一區間了.
出於上述考慮,會寫出:
if (C(mid)) l = mid; else r = mid - 1;
而一旦你的問題採用了這種形式,就必須有:
int mid = l + r + 1>> 1;
才能保證不會陷入死迴圈.
AC程式碼:
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int L, N, M, s[50010];
// 思路: s[0]為0, 即起點, s[N + 1]為L, 即終點
// 對於s[i], 檢查其前方有多少個石頭是在條約距離x以內(不含)的, 每有一個石頭則移除之, 並且i遞增以在下一次外層迴圈中跳過已經視為移除的石頭 bool C(int x) {int ct = 0, tmp; for (int i = 0; i <= N; i++) { // 這裡的寫法並不好, 可以寫出O(n)的C(x), 參考洛谷題解區, 點選文首圖片跳轉 tmp = 0; for (int j = 1; j + i <= N + 1 && s[i + j] - s[i] < x; j++) tmp++; ct += tmp; i += tmp; } return ct <= M; } int main() { cin >> L >> N >> M; for (int i = 1; i <= N; i++) cin >> s[i]; s[N + 1] = L; int l = 0, r = 1000000000; while (l < r) { int mid = l + r + 1 >> 1; if (C(mid)) l = mid; else r = mid - 1; } cout << l << endl; return 0; }
在這題以外,如果以後根據實際問題寫出了:(僅對於整數)
if(...) l = mid + 1; else r = mid;
那麼必須有:
int mid = l + r >> 1;
以保證不會陷入死迴圈.