题目大意:给出河的宽度L和N块石头,现在要求移除M块石头,使得石头间的距离的最小值达到最大(起点和终点都有一块石头,但这两块石头不能移除)
解题思路:最小值的最大值,肯定用二分了
如果存在最优的距离,那么移走的石头数量肯定刚好是M块的
枚举的时候判断移除石头的数量,只需要从起点开始枚举,然后计算一下在最小跳跃距离内的石头有几个,有几个就移除几个,最后判断移除了多少个石头
如果移走的数量大于M,就表示所枚举的长度偏大
如果移走的数量小于M,就表示所枚举的长度偏小
有一种比较特殊的情况,就是N = M的情况,这种情况下枚举的很多长度都是符合的,但是答案只有一个,所以移除数量等于M的时候不代表已经找到解了,应该再长度加1判断一下
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int L; //河总长
int n; //河中石头数(除起点S和终点外E)
int m; //移除石头数
while(cin>>L>>n>>m)
{
/*Input & Initial*/
int* dist=new int[n+2]; //第i块石头到起点石头的距离为dist[i]
dist[0]=0; //起点S
dist[n+1]=L; //终点E
int low=0; //上界(一次跳跃的最短距离)
int high=L; //下界(一次跳跃的最大距离)
for(int i=1;i<=n;i++)
{
cin>>dist[i];
}
sort(dist,dist+(n+2));
//根据石头到S的距离升序排列
while(low<=high)
{
int mid=(low+high)/2; //对最大跳和最小跳的距离折中,二分查找mid相对于最优解是偏大还是偏小
//假设mid是移除m个石头后的最短距离
int delrock=0; //利用当前的mid值能移除的石头数
int sum=0; // 这里是 连续距离的累加值
//当在第i个距离累加后sum
for(int i=1;i<=n+1;)
{
if( (sum+=dist[i]-dist[i-1]) < mid)//如果值<mid,则需要去掉一个石头
{
i++;
delrock++;
}
else //当从第i个距离累加到i+k个距离后,若sum>mid,则k个距离作为一段
{
i++;
sum=0; //sum置0,从第i+k+1个距离重新累加
}
}
if(delrock<=m) //本题难点之一:即使delrock==m也不一定找到了最优解
//有一种比较特殊的情况,就是delrock = M的情况,这种情况下枚举的很多长度都是符合的,但是答案只有一个,所以移除数量等于M的时候不代表已经找到解了,应该再长度加1判断一下
low=mid+1; //用当前mid值移除的石头数小于规定数,说明mid偏小
else
high=mid-1; //反之mid偏大
}
printf("%d\n",low-1);
}
return 0;
}