題目鏈接: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;
}