【题目描述:】
陶陶是个贪玩的孩子,他在地上丢了A个瓶盖,为了简化问题,我们可以当作这A个瓶盖丢在一条直线上,现在他想从这些瓶盖里找出B个,使得距离最近的2个距离最大,他想知道,最大可以到多少呢?
【输入格式:】
第一行,两个整数,A,B。(B<=A<=100000)
第二行,A个整数,分别为这A个瓶盖坐标。
【输出格式:】
仅一个整数,为所求答案。
【算法分析:】
关于单调性的感性证明:
如果一个mid作为最大值时所选的瓶盖数量 x≥B,即最大值过小导致选取瓶盖过多,
则答案一定在[mid + 1, r]内
否则答案在[l, mid]内
满足单调性,可以二分答案。
关于check函数:
在check一个数mid时,check函数返回mid作为最大值时的选取瓶盖数量,
这里可以做一个优化,即check返回一个bool类型
当选取的瓶盖数量超过读入的B时,直接返回1,表示二分[mid + 1, r]这个区间
将数据从小到大排序来实现check函数:
读入有n个元素的序列a,最大值为max
利用贪心的思想,由于是求最少选取的瓶盖数量,能不选这个瓶盖的时候就不选
last表示上一次选取的瓶盖位置
当且仅当 ai - last > max 时,不选取ai会导致最大值变大,last更新成ai,选取的瓶盖个数+ 1
关于上下界的初始化:
二分时上下界的初始选择可以是[0, INF],最多进行31次查找。
只选取两个瓶盖时的最大值为amax - amin ,由于数据已经排好序,上界直接初始化成 an - a1 即可
【代码:】
1 //丢瓶盖 2 #include<iostream> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 7 const int MAXN = 100000 + 1; 8 const int INF = 0x7fffffff; 9 10 int n, m; 11 int a[MAXN]; 12 13 bool check(int num) { 14 int ret = 1, last = a[1]; 15 for(int i = 2; i <= n; i++) { 16 if(a[i] - last > num) last = a[i], ret++; 17 if(ret >= m) return true; 18 } 19 return ret >= m; 20 } 21 int main() { 22 scanf("%d%d", &n, &m); 23 int l = 0, r = INF; 24 for(int i = 1; i <= n; i++) { 25 scanf("%d", &a[i]); 26 } 27 sort(a + 1, a + n + 1); 28 r = a[n] - a[1]; 29 while(l < r) { 30 int mid = (l + r) >> 1; 31 if(check(mid)) l = mid + 1; 32 else r = mid; 33 } 34 printf("%d ", l); 35 }