#include <iostream>
#include<algorithm>
using namespace std;
const int N=1e6+9;
int x[N];
int n,m;
int check(int mid)
{
int res=0;
for(int i=1,lst=0;i<=n;++i)
{
if(lst&&x[i]-x[lst]<mid)continue;
res++,lst=i;
}
return res;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>x[i];
sort(x+1,x+1+n);
int l=0,r=1e9;
while(l+1!=r)
{
int mid=(l+r)/2;
if(check(mid)>=m)l=mid;
else r=mid;
}
cout<<l<<endl;
return 0;
return 0;
}
在这个二分搜索算法中,我们要找到的是满足条件的最大间隔值,即在数组x
中能选取元素的最大间隔值使得可以选择至少m
个满足条件的点。这里我们使用了两个变量l
和r
分别作为搜索区间的下限和上限。
关键点在于循环退出时,边界l
和r
是相邻的整数,即�=�+1。在循环过程中:
- 当发现某个间隔值mid使得可以选择出 >= m 个满足条件(间隔 >= mid)的点时,我们知道这个mid可能不是最大的可行解,但至少它是一个可行解,所以我们将下限提升到mid(l = mid)。
- 如果这个间隔值mid无法保证选择出 >= m 个满足条件的点,则说明mid太大了,我们需要减小可接受的间隔。由于mid不可行,所以上限被压缩到了mid前面一个数(r = mid)。
当上下界相邻时�+1=�,此时循环结束:
l
表示在确保可以选择出至少m个元素时当前已知的最大可行解。(因为如果更大会导致选不出足够的元素)r
表示第一个已知的不可行解。(因为比r - 1 = l
大一位就已经选不出足够元素了)
由于要求最大可能的有效间隔,则距离应该取当前已知对于所寻找约束来说最右端(largest)且有效(feasible)的位置。此位置就是变量 l
, 因此代码末端输出 l
. 这样做基于如下逻辑:
最后check
函数返回值即是在给定间隔 mid
下按规则最多可以放置几棵数木。
- 如果存在更宽松且仍然合理(更大临界但仍然有M或超过M个结果) 的情况,在向右查找过程 (增加L) 中一定早就遇到并已终止循环;
- 否则 (附近没有更好答案),当前L是迄今为止找到并确保合理性 (至少M结果) 的边界,在给定信息下尽可能稀疏。
-
int check(int mid) { int res=0; // 初始化结果res,代表能够根据给定间距mid种植的树的数量 for(int i=1,lst=0;i<=n;++i) // 开始遍历所有的位置,并使用变量lst记录上一次种树的位置。初始设为0(没有种过树) { if(lst&&x[i]-x[lst]<mid)continue; // 如果前一个点存在并且当前点与前一个种树点的距离小于mid,则跳过当前点。即:当前位置不能种树。 res++; // 否则,在当前点种树,结果res加1 lst=i; // 并且更新lst为当前点,也就是记录这个已经被用来种树的位置。 } return res; // 遍历完所有位置后,返回总共能够种植的树木数量res。 }
这个
check
函数通过在所有可能的位置上模拟种植树木,来确定对于特定间隔mid
是否可能至少种植下m
株树。具体逻辑是:
- 函数接受一个参数
mid
表示任意两棵相邻树之间至少需要保持的距离。 - 它会一次考虑每一个可能放置一棵树的地方(包含n个)。
- 对于每个可能放置一棵数的地方:
if
语句检查这个地方是否能放置一棵数(保证与上一棵种下的数至少有mid
的距离)。- 如果不能,就继续检查下一个地方。
- 如果可以,则将该地方作为新的上次放置数木之处,并增加计数器
res
来表示多了一棵符合条件の数木。