洛谷P1824 進擊的奶牛 題解 二分答案

題目鏈接:https://www.luogu.com.cn/problem/P1824

題目大意:

本題相當於在 \(n\) 個數中選 \(c\) 個數,使得這 \(c\) 個數中相差最小的兩個數之差儘可能地大。

解題思路:

我們首先可以給 \(a_1 \sim a_n\) 從小到大排一下序(這裏有點貪心的思想,你會發現很多涉及貪心的問題在排序之後解決起來都會方便很多)。

然後我們可以開始設計一個 bool check(int x) 函數,用來判斷:在任意相鄰兩個數之差都 \(\ge x\) 的情況下,是否能夠選夠 \(c\) 個數。

接着咱們就來判斷 check(x) 能否成立:

首先,最優解 選 \(a_1\) 肯定沒毛病,

假設有一個最優方案,第一個選的數是 \(a_2\),第二個選的數是 \(a_5\),即滿足 \(a_5 - a_2 \ge x\),那麼 \(a_5 - a_1 \ge x\) 肯定也成立。

在選擇了 \(a_1\) 的情況下,我們可以從 \(a_2\) 開始到 \(a_n\) 選剩下來的數,對應前 \(i\) 個數,我肯定是希望儘可能選最多數字,因爲這樣能夠騰給後面的數字的空間就更大。

假設我上一個選擇的數是 \(a_p\),對於當前的 \(a_i\) ,若 \(a_i - a_p \ge x\)(相差 \(\ge x\) 了),就選擇 \(a_i\),同時更新 \(p\)\(i\)\(p\) 時刻對應當前最後一個選的數的下標)。

在整個過程中統計一個一共選了多少個數即可,只要 \(\ge c\) 個,check(x) 函數就返回 true;否則,返回 false。

check函數代碼:

bool check(int x) {
    int p = 1, cnt = 1; // p表示最近一個選擇的點, cnt表示目前已選擇的點數
    for (int i = 2; i <= n; i++) {
        if (a[i] - a[p] >= x) {
            p = i;
            cnt++;
        }
    }
    return cnt >= c; // 選夠c個就返回true
}

有了 check 函數接下來就是二分答案了。

可以發現,存在一個最大的整數 \(x\),從 \(1\)\(x\) check 函數都會返回 true;從 \(x+1\) 開始 check 函數就都會返回 false 了。

完整代碼:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, c, a[maxn];

bool check(int x) {
    int p = 1, cnt = 1;
    for (int i = 2; i <= n; i++) {
        if (a[i] - a[p] >= x) {
            p = i;
            cnt++;
        }
    }
    return cnt >= c;
}

int main() {
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; i++)
        scanf("%d", a+i);
    sort(a+1, a+n+1);
    int l = 0, r = 1e9, res;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (check(mid))
            res = mid, l = mid + 1;
        else
            r = mid - 1;
    }
    printf("%d\n", res);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章