一、问题描述
二、问题分析
通过题目的意思我们可以捕获到几个需要注意的关键点:
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;
}