POJ 3258 River Hopscotch

每年奶牛们都要举办各种特殊版本的跳房子比赛,包括在河里从一块岩石跳到另一块岩石。这项激动人心的活动在一条长长的笔直河道中进行,在起点和距离起点 L 远的终点各有一块岩石 (1 ≤ L ≤ 10^9)。在起点和终点之间,有 N 块岩石 (0 ≤ N ≤ 50000),每块岩石与起点的距离分别为 Di (0 < Di < L)。

在比赛过程中,奶牛轮流从起点出发,尝试到达终点,每一步只能从一块岩石跳到另一块岩石。当然,实力不济的奶牛无法抵达终点,在河中间就退出比赛了。

农夫约翰为他的奶牛们感到自豪并且年年都观看了这项比赛。但随着时间的推移,看着其他农夫的胆小奶牛们在相距很近的岩石之间缓慢前行,他感到非常厌烦。他计划移走一些岩石,使得从起点到终点的过程中,最短的跳跃距离最长。他可以移走除起点和终点外的至多 M 块岩石 (0 ≤ M ≤ N)。

请帮助农夫约翰确定:移走这些岩石后,最短跳跃距离的最大值是多少?

输入

第 1 行包含以单个空格分隔的三个整数 L, N, M。

第 2 到 N + 1 行,每行一个整数,表示每个岩石与起点的距离。不会有两个岩石出现在同一个位置。

输出

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

示例输入
25 5 2
2
14
11
21
17
示例输出
4
提示

在移除位于 2 和 14 的两块岩石之后,最短跳跃距离达到了最大值 4 (从 17 到 21,或从 21 到 25)。

一个典型的最小值最大化问题,只不过这道题判断有点麻烦,这里用一个num[]数组来确定二分出来的最小距离x是否满足;

我们用stone[]数组来表示石头,把stone[0] = 0,stone[N+1] = L,然后排序一下,就可以O(n)判断了,所以这一题二分很简单,主要是二分的判断。

//判断删除M块石头,是否可以跳跃。
int judge(ll x) {
    int cnt = 0;
    for(int i = 0; i <= N+1; i++) { //因为石头的初始值不能改变,所以赋值给num[]数组 
    	num[i] = stone[i];
	}
    for(int i = 1; i <= N+1; i++) { //这一点很好,我一开始写的很麻烦 
    	if(num[i] - num[i-1] < x) { //如果两个石头之间的距离小于x 
    		num[i] = num[i-1];      //去掉后面一个石头,然后把前面的时候赋值给它,再往后比较 
    		cnt++;                  //去掉石头的数目 
		}
    }
    if(cnt <= M) return 1;
    else return 0;
}

具体代码如下:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
ll L, N, M;
ll num[50005];
ll stone[50005];
int judge(ll x) {
    int cnt = 0;
    for(int i = 0; i <= N+1; i++) { //因为石头的初始值不能改变,所以赋值给num[]数组 
    	num[i] = stone[i];
	}
    for(int i = 1; i <= N+1; i++) { //这一点很好,我一开始写的很麻烦 
    	if(num[i] - num[i-1] < x) { //如果两个石头之间的距离小于x 
    		num[i] = num[i-1];      //去掉后面一个石头,然后把前面的时候赋值给它,再往后比较 
    		cnt++;                  //去掉石头的数目 
		}
    }
    if(cnt <= M) return 1;
    else return 0;
}
int main() {
    scanf("%lld %lld %lld", &L, &N, &M);
    stone[0] = 0; stone[N+1] = L;       //两岸 
    for(int i = 1; i <= N; i++) {
        scanf("%lld", &stone[i]);
    }
    sort(stone, stone+N+1);
    ll left = 0, right = L, mid;
    while(right > left+4) {             //不要考虑边界问题了 
        mid = (left + right)/2;
        if(judge(mid)) left = mid;
        else right = mid;
    }
    for(ll i = right; i >= left; i--) {
        if(judge(i)) {
            printf("%lld\n", i);
            break;
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值