NOIP2015提高组---跳石头

189 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情

[NOIP2015 提高组] 跳石头

题目背景

一年一度的“跳石头”比赛又要开始了!

题目描述

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 NN 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 MM 块岩石(不能移走起点和终点的岩石)。

输入格式

第一行包含三个整数 L,N,ML,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L1L \geq 1NM0N \geq M \geq 0

接下来 NN 行,每行一个整数,第 ii 行的整数 Di(0<Di<L)D_i( 0 < D_i < L), 表示第 ii 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出格式

一个整数,即最短跳跃距离的最大值。

样例 #1

样例输入 #1

25 5 2 
2
11
14
17 
21

样例输出 #1

4

提示

输入输出样例 1 说明

将与起点距离为 221414 的两个岩石移走后,最短的跳跃距离为 44(从与起点距离 1717 的岩石跳到距离 2121 的岩石,或者从距离 2121 的岩石跳到终点)。

数据规模与约定

对于 20%20\%的数据,0MN100 \le M \le N \le 10
对于 50%50\% 的数据,0MN1000 \le M \le N \le 100
对于 100%100\%的数据,0MN50000,1L1090 \le M \le N \le 50000,1 \le L \le 10^9

分析

这道题目题意就是在长度为LL的数轴上,给nn个点,删去最多mm个点,使得剩下的点当中两两之间的距离的最小值最大。如果暴力枚举,会严重超时,所以我们应该考虑二分答案,二分一个距离,看除去多少石头能使得两点之间的距离都能在小于这个距离的时候尽可能的大。 这里有一个很重要的要点,要把右端点LL加点的数组,不然过不了额外测试点(亲测) 如下代码:

bool check(int x){
   int cnt=0;//移去石头的个数
   ll ans=0;//没有移走的石头的坐标(用来跟新)
   for(int i=1;i<=n+1;i++){
   	if(d[i]-ans<x){//如果当前的石头的坐标减去前一个没有移动的石头的坐标比x小,那么说明可以移走这块石头
   		cnt++;
   	}
   	else{//否则不能移除这一块石头
   		ans=d[i];//更新当前不能移走的石头
   	}
   }
   return cnt<=m;
}

接下来贴上完整代码

完整代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string> 
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack> 
#include <cmath>
#include <iomanip>
#define ll long long
#define AC return
#define Please 0
using namespace std;
const int N=101000;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef unsigned long long ull; 
int dist,n,m,d[N];
inline int read(){//快读 
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9'){
        x=x*10+ch-'0'; 
        ch=getchar();
    }
    AC x*f;
}
bool check(int x){
	int cnt=0;
	ll ans=0;
	for(int i=1;i<=n+1;i++){
		if(d[i]-ans<x){
			cnt++;
		}
		else{
			ans=d[i];
		}
	}
	return cnt<=m;
}
int main(){
	dist=read(),n=read(),m=read();
	for(int i=1;i<=n;i++){
		d[i]=read();
	}
	d[n+1]=dist;//很重要的一点要加上端点,因为端点也是一块石头,只不过不能移走
	ll l=1,r=dist;
	while(l<r){
		ll mid=l+r+1>>1;
		if(check(mid)){//如果当前距离对应移走的石头数量小于等于最多要移走的石头数量m,那么说明这个距离还能变大或者等于答案
		     l=mid;
		} 
		else r=mid-1;//如果大于这个数量,那么就说明这个值要变小
	}
	cout<<l<<endl;//最后输出答案。
    AC Please;
}

希望能帮助到大家(QAQQAQ)!