题目描述:
链接:https://ac.nowcoder.com/acm/contest/3402/E
来源:牛客网
数据已修改,比赛时的所有提交已经进行了rejudge。
假面骑士要过河,恰好河的两岸间有N(0 ≤ N ≤ 50,000)块石头在同一条直线上。他已经知道了河的宽度L(1 ≤ L ≤ 1,000,000,000)和每一块石头离假面骑士所在河岸的距离Di(0 < Di < L),石头不会发生重叠。
可是假面骑士在与怪物的激烈战斗之后,留下了后遗症。他不能忍受跨越任何一块石头,所以他只能一块一块地按距离顺序踩着石头过河。皮卡丘是他的好朋友,皮卡丘为了帮助他,已经使用十万伏特去掉了M (0 ≤ M ≤ N)块石头,不过皮卡丘没有告诉假面骑士到底去掉了哪些位置的石头。
假面骑士想知道在过河的过程中,他需要跨越的最小距离最大可能是多少。
输入描述:
有多组测试,保证
∑
N
i
≤
5
∗
1
0
6
\sum N_i \leq 5*10^6
∑Ni≤5∗106;
每个测试的第一行是河的宽度L,石头总数N,被消灭石头的个数M
第二行有N个数,表示每块石头到假面骑士所在河岸的距离
输出描述:
输出在所有可能的情况下,需要跨越的最小距离最大可能是多少
输入样例:
5 2 1
3 4
25 5 2
2 14 11 21 17
输出样例:
2
4
核心思想:
二分距离,对于每一个距离x:
以cnt记录被删除石头的数目,
先自近向远遍历,如果石头i和上一个被保留下的石头的距离不小于x,则石头i可以被保留,否则删掉。
再自远向近遍历,如果石头i与目的河岸的距离小于x,则删除石头i,否则遍历结束。
若cnt不大于m,则x可行,否则x不可行,分情况缩小二分区间。
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=5e4+20;
int d,n,m,a[N],b[N];//a存原数组,b存保留下的数
bool fun(int x)
{
int p=0,cnt=0;
b[p++]=0;
for(int i=1;i<=n&&cnt<=m;i++)//自近向远选数
if(a[i]-b[p-1]<x) cnt++;
else b[p++]=a[i];
if(cnt>m) return 0;
for(int i=p-1;i>0&&d-b[i]<x;i--,cnt++);//自远向近删数
if(cnt>m) return 0;
return 1;
}
int main()
{
while(cin>>d>>n>>m)
{
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);//a数组原本是乱序的
int l=0,r=d,ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(fun(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}