牛客3402E-一步两步是魔鬼的步伐-二分

题目描述:

链接: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 ;
每个测试的第一行是河的宽度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;
}
发布了144 篇原创文章 · 获赞 135 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Nothing_but_Fight/article/details/103760562