题目链接

https://vjudge.net/contest/397323#problem/D

解题思路

最大值最小(或最小值最大),暂时我就知道两种方法一个是二分,一个是贪心。
本题一看就可以二分,所以二分试了试,真行。
稍微有点难度的就是如何去判断枚举到的这个距离到底能不能安排B个。
我也卡了一会,因为一直不知道到底要不要一定选第一个,如果不一定选第一个,那么就要枚举第一个选取的石子的位置,这样一来时间复杂度明显太大;如果一定选第一个,那么就方便多了,只要从第一个开始,判断下一个是不是与它相距超过枚举的距离,超过就再从当前点往后判断,没超过就跳过当前点,判断当前点的下一个点是否满足条件。
但是!如何确定第一个石子一定选呢?
首先,你想一下,要让最小距离尽量大,是不是就得尽量让石子分布的稀疏,怎么让石子尽量稀疏,是不是就得让石子分布的区间更大,怎么让石子分布的区间更大,是不是就得把第一个选了,后面剩下的区间才大。
再者,如果第三个石子能够被选,假设第二个石子(第三个石子前面那个石子)能被选,是不是说明如果不选第二个选第一个也一定可行,因为第一个石子到第三个石子的距离必然大于第二个石子到第三个石子的距离。这样也可以间接不严谨地推断出一定要选第一个。

AC代码(高*格呈现,美观可读性差点)

#include<bits/stdc++.h>
#define ll long long
#define BS BinarySearch
using namespace std;
const int N=1e5+10;
ll pos[N];
int A,B,ans;
void create(){
    cin>>A>>B;
    for(int i=1;i<=A;i++){
        cin>>pos[i];
    }
    sort(pos+1,pos+A+1);
}
int Dis(int m){
    int cnt=1,p=1;
    for(int i=1;i<=A;i++){
        if(pos[i]-pos[p]>=m) p=i,cnt++;
    }
    if(cnt>=B) return 1;
    else return 0;
}

void BS1(){
    int r=pos[A]-pos[1],l=0;
    while(l<=r){
        int m=(l+r)/2;
        if(Dis(m)) l=m+1,ans=m;
        else r=m-1;    
    } 
}
void BS2(){
    int r=pos[A]-pos[1]+1,l=0;
    for(int i=1;i<=100;i++){
        int m=(l+r)/2;
        if(Dis(m)) l=m,ans=m;
        else r=m;    
    }
}
void Output(){
    cout<<ans<<endl;
}
void solve(){
    create();
    BS1();
    //BS2();
    Output();
}
int main(){
    solve();    
}

AC代码(未修改前,可读性高)

#include<bits/stdc++.h>
#define ll long long
#define BS BinarySearch
using namespace std;
const int N=1e5+10;
ll pos[N];
int A,B,ans;
int Dis(int m){
    int cnt=1,p=1;
    for(int i=1;i<=A;i++){
        if(pos[i]-pos[p]>=m) p=i,cnt++;
    }
    if(cnt>=B) return 1;
    else return 0;
}
int main(){
    cin>>A>>B;
    for(int i=1;i<=A;i++){
        cin>>pos[i];
    }
    sort(pos+1,pos+A+1);
    /*
    //BinarySearch1
    int r=pos[A]-pos[1],l=0;
    while(l<=r){
        int m=(l+r)/2;
        if(Dis(m)) l=m+1,ans=m;
        else r=m-1;    
    } 
    */
    //BinarySearch2
    int r=pos[A]-pos[1]+1,l=0;
    for(int i=1;i<=100;i++){
        int m=(l+r)/2;
        if(Dis(m)) l=m,ans=m;
        else r=m;    
    }
    cout<<ans<<endl;
}

两个BinarySearch

虽然AC了,但是我每次做二分都比较喜欢对比一下这两种二分方法的区别。
BS1二分的条件是l<=r,而BS2的条件是判断的次数。
要想BS2也AC,其开始定义的r=pos[A]-pos[1]+1,举个特殊情况的例子:2 2 1 10
此时AC代码输出9,而r=pos[A]-pos[1]的BS2代码却输出8
模拟一下过程不难发现,区间左端点枚举不到9,所以ans也无法被赋值为9。
把初始区间左端点定义为+1,就解决了这个问题,(右端点也得是最小情况-1,但是0就已经是最小情况1减去1了)

总结

俩BS的不同,和不同的原因才是我真正想记录的,题目本身不难。