砍树(二分法)

砍树

题目描述
伐木工人米尔科需要砍倒 M 米长的木材。这是一个对米尔科来说很容易的工作,因为他有一个漂亮的新伐木机,可以像野火一样砍倒森林。

米尔科的伐木机工作过程如下:米尔科设置一个高度参数 H(米),伐木机升起一个巨大的锯片到高度 H,并锯掉所有的树比 H 高的部分(当然,树木不高于 H 米的部分保持不变)。米尔科就得到树木被锯下的部分。

例如,如果一行树的高度分别为20,15,10 和17,米尔科把锯片升到 15 米的高度,切割后树木剩下的高度将是15,15,10 和 15,而米尔科将从第 1 棵树得到 5 米,从第 4棵树得到 2米,共得到 7 米木材。

米尔科非常关注生态保护,所以他不会砍掉过多的木材。这正是他为什么尽可能高地设定伐木机锯片的原因。帮助米尔科找到伐木机锯片的最大的整数高度 H,使得他能得到木材至少为 M米。换句话说,如果再升高 1 米,则他将得不到 M 米木材。

输入格式
第1行 2 个整数 N 和 M,N表示树木的数量,M 表示需要的木材总长度。
第2行 N 个整数表示每棵树的高度。

输出格式
1 个整数,表示砍树的最高高度。
输入输出样例
输入
5 20
4 42 40 26 46
输出
36
说明/提示 :1≤N≤10 ,1≤M≤2000000000,每棵树的高度≤1000000000,所有木材长度值和>=M
题解
通过读题,我们发现随着砍树的高度的增加,得到的木材就会越少,这是单调的关系,所以我们应该可以想到二分法(有单调性一定可以二分,没有单调性也可能可以二分)。
我们先来说说二分法

我的理解,二分就是取一个点,使一条线段一分为二,线段左边不能满足性质,线段右边可以满足这个性质,如图(红色表示不满足性质,绿色表示满足性质)由于是整数二分,所以分界点有两个在这里插入图片描述
这里是我用的二分的模板(借用y总的模板哈哈哈),这个模板可以避免很多二分的边界问题

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

作者:yxc
链接:https://www.acwing.com/blog/content/277/
来源:AcWing

具体我们来看题,这道题里面,可以得到的木材是随着砍的高度变化而单调变化的,所以跟上述模板,我们要找到可以砍的高度的边界还有性质。
左边界我们设为零
右边界当然是最高的树的高度
性质得到的木材是否超过规定的木材
于是就可以上代码啦!

#include<bits/stdc++.h>
using namespace std;
const int n = 1e6+10;//值开大一点,避免数组越界 
typedef long long LL;//数值很大,所以我们要开long long,不开要爆 
LL N,M,r,l;
LL tree[n];
int main()
{
	cin >> N >> M;
	for(int i = 0; i < N; i++)
	{
		cin >> tree[i];
	    r = max(r,tree[i]);//找最大的一棵树 ,确定右边界 
	}
	while(l < r)//左边界取0,只要小于这些树的高度就行 
	{
		int mid = l + r + 1 >> 1;//二分,找到中间点 
		LL sum = 0;//表示从mid高度砍后可以得到的木材,一定记得每一次都清零 
		for(int i = 0; i < N; i++)
		if(tree[i] >= mid) sum = sum + tree[i] - mid;//如果这棵树比砍的高度高,可以得到木材 
	    if(sum >= M) l = mid ;//如果得到的木材大于了规定的木材,说明砍的高度小了,所以要从这个值的右边取,更新左边界 
		else r = mid - 1; //反之更新右边界 
	}
	cout << l << endl;//输出砍的最适合的高度,l,r都可以 
	return 0;
 } 

第二次写博客,很多问题可能没有解决好,希望大佬们评论交流,谢谢!

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值