基础算法_最小化最大值(二分答案)

题意:

1243 排船的问题

一个码头中有N艘船和N个木桩,船的长度为2*X,码头的宽度为M,N个木桩的位置(相对码头左岸的位置)会在数据中给出。

把所有的船排成一排,船和船之间不能重叠,也不能超出码头的两岸。
船和木桩之间用绳子连接,并且1个木桩只能栓1条船,绳子的一头拴在木桩上,另一头拴在船的中间。

船中间到木桩的距离,就是所需的绳子的长度。由你安排船的位置,使得所用到的最长的绳子最短。输出这个最短的长度,如果码头排不下所有船则输出-1。
在这里插入图片描述
例如:N = 3, X = 2, M = 16。三个木桩的位置为:1 3 14。船的长度为2*X = 4。你可以将三艘船放在2 6 14(指的是船中间所处的位置),这样船和船之间既没有重叠,并且所用的最长的绳子最短,长度为3,即第2艘船到第二根木桩的距离。

输入
第1行:3个数N X M,中间用空格分隔(1 <= N <= 50000, 1 <= X <= 10^9, 1 <= M <= 10^9)。
第2 - N + 1行:每行1个数Pi,对应木桩的位置(0 <= Pi <= Pi+1 <= M),并且给出的数据是有序的。

输出
输出最长绳子的最小值。如果码头排不下所有船则输出-1。

输入样例
3 2 16
1
3
14

输出样例
3

思路:

二分答案
直接好家伙,居然是二分答案,
还是太菜了,连最小化最大值用二分答案写都忘了,更离谱的是读错了题,wuwuwuwuwu。。。菜,永恒的菜。。。。。。
(1)这道题如果2 * n * x > m 直接输出 -1,肯定是木有问题的,对吧!!
(2)二分没什么问题,关键在于判断,怎么判断呢?????
贪心写
就像这道题,所有的船都是尽量往最左边放的。。。刚开始看到尽量往最左边放的时候我一脸懵。。
首先我们一定要先明确要求的是“船中间到木桩的距离,就是所需的绳子的长度。由你安排船的位置,使得所用到的最长的绳子最短。输出这个最短的长度”。。n个木桩,n条船,什么意思,说明一对一呗,所以对于每条船,我们现在假设第一条船吧。
1. 船的中间在木桩左边或在木桩位置的情况:
当前head = 0,x = 2,p[1] = 5 , mid = 2可以得到head + x + mid < p[1],这个时候是可以放船的。。但是船的中间只能和p[1]相距2,所以head = max(head, p[1] - mid - x)更新为1。tail加上2 * x就可以了

2.在木桩右边的情况:
首先,可想而知,如果head + x - p[i] > mid 现在所需要的绳子的长度要大于mid,mid就不是当前最大的了,return 0就可以。。。。不然就直接head不变,tail加2 * x就好了

注意在匹配每一条船和每一个木桩的时候head == tail。。。
3.最后比较tail和m的大小就好了

代码实现:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<string>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
ll n,x,m;
ll p[maxn];
ll ans = 1e14;
bool check(ll xx){
	ll start = 0;
	ll tail = 0;
	for(int i = 1;i <= n;i++){
		start = tail;
		if(start + x + xx <= p[i]){
			start = max(start,p[i] - xx - x);
			tail = start + 2 * x;
		}
		else{
			if(start + x - p[i] > xx){
				return 0;
			}
			tail = start + 2 * x;
		}
	}
	if(tail > m) return 0;
	else return 1;
}
int main(){
	scanf("%lld%lld%lld",&n,&x,&m);
	for(ll i = 1;i <= n;i++){
		scanf("%lld",&p[i]);
	}
	if(n * 2 * x > m){
		printf("-1\n");
	}
	else{
		ll l = 0,r = 1e14;
		ll mid;
		ll ans = 0;
		while(l <= r){
			mid = (l + r) / 2;
			if(check(mid)){
				r = mid - 1;
				ans = mid;
			}
			else{
				l = mid + 1;
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值