砍树问题

一、问题描述

在这里插入图片描述在这里插入图片描述

二、问题分析

通过题目的意思我们可以捕获到几个需要注意的关键点:
1.砍树的方式为横向截取


2.砍树所得的和的总量要恰当,不能过多,也不能过少

解题思路

所以可以通过分治法(利用二分法查找的原理)来解决这个问题(并非传统的分治,我们考虑从最高的一棵树入手,对齐进行分治,来找寻解决方案)

1.先将树进行排序(此处可调用c++提供的sort函数)


2.用分治法处理最高的树(以最高的树为参照来找寻砍树的切割点)


以当前mid为切割点来进行砍树,并统计当前切割点所得的树木的数量,与题目所需的数量进行比较

分析当前切割点:

A:如果当前切割点所得的树木的总和大于实际所需的树木总和,则应该上升切割点(left=mid+1,right=right)

B:如果当前切割点所得的树木的总和小于实际所需的树木总和,则应该下降切割点(left=left,right=mid-1)

3.当left>right(终结条件)left>right 即代表判断完毕

4.注意:我们还需要两个数组,一个用于存放每次切割点所砍得的树木的总和,一个用于存放对应的切割点所对应的mid

6.当满足前一次mid所得的树木总和大于实际需求,后一次mid所得的树木总和小于实际需求时,即为最佳切割点

代码实现

#include<iostream>
#include<algorithm>//用于调用sort排序函数(默认升序)

using namespace std;

int a[20];//用于存放 每次砍的树木的总和
int b[20];//用于存放 每次砍树的切割点mid
int time = 0;//统计砍树的次数

int cutTree(int N, int M, int left, int right, int tree[])
{
	int sum = 0;//所砍的木材长度之和

	if (left <= right)//终结条件 left>right 查找结束,无法再继续查找可执行的mid
	{
		int mid = (left + right) / 2;//切割点mid
		for (int i = 0; i < N; i++)
		{
			if (tree[i] > mid)
				sum = sum + (tree[i] - mid);//统计当前mid所能砍的树的树木总和
		}
		//保存每一次mid的砍树的总和和mid
		a[time] = sum;
		b[time] = mid;
		time++;
		if (sum > M)
		{
			//当前mid所砍的树的总和大于所需,则mid的高度往上提 即left=mid+1
			cutTree(N, M, mid + 1, right, tree);
		}
		else
		{
			//当前mid所砍的树的总和小于所需,则mid的高度往下降 即right=mid-1
			cutTree(N, M, left, mid - 1, tree);
		}
	}
	else
	{
		for (int i = 0; i < time; i++)
		{
			//输出条件 前一个sum小于M 但后一个sum大于M 说明此时的sum是最优解 mid即为最佳切割点
			if (a[i]<M && a[i + 1]>M)
			{
				//返回当前位置所对应的mid 即最佳切割点
				return b[i + 1];
			}
		}
	}
}

int main()
{
	int N, M;//N:树木的数量 M:需要的木材总长度
	cin >> N;
	cin >> M;

	int tree[100];//存放每棵树的高度
	for (int i = 0; i < N; i++)
		cin >> tree[i];
	sort(tree, tree + N);//调用sort函数对tree数组进行排序(默认升序)

	int ans = cutTree(N, M, 0, tree[N - 1], tree);
	cout << ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值