1. 程式人生 > 其它 >Problem C: 簡單的數值統計

Problem C: 簡單的數值統計

答案具有單調性,即存在一個最優解(最短條約距離的最大值),使得大於此解值的值都需要移走多於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;

以保證不會陷入死迴圈.