2025 - 02 - 19 - 第 56 篇
作者(Author): 郑龙浩 / 仟濹(CSND)
【二分搜索】
文章目录
洛谷P2440 木材加工
题目背景
要保护环境
题目描述
木材厂有 n n n 根原木,现在想把这些木头切割成 k k k 段长度均为 l l l 的小段木头(木头有可能有剩余)。
当然,我们希望得到的小段木头越长越好,请求出 l l l 的最大值。
木头长度的单位是 cm \text{cm} cm,原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。
例如有两根原木长度分别为 11 11 11 和 21 21 21,要求切割成等长的 6 6 6 段,很明显能切割出来的小段木头长度最长为 5 5 5。
输入格式
第一行是两个正整数 n , k n,k n,k,分别表示原木的数量,需要得到的小段的数量。
接下来 n n n 行,每行一个正整数 L i L_i Li,表示一根原木的长度。
输出格式
仅一行,即 l l l 的最大值。
如果连 1cm \text{1cm} 1cm 长的小段都切不出来,输出 0
。
输入输出样例 #1
输入 #1
3 7
232
124
456
输出 #1
114
说明/提示
数据规模与约定
对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ 1 0 5 1\le n\le 10^5 1≤n≤105, 1 ≤ k ≤ 1 0 8 1\le k\le 10^8 1≤k≤108, 1 ≤ L i ≤ 1 0 8 ( i ∈ [ 1 , n ] ) 1\le L_i\le 10^8(i\in[1,n]) 1≤Li≤108(i∈[1,n])。
变量
cpp
//main函数
moods - 每个木头长度
num - 木头数量
equal_length - 等分的木头长度
equal_num - 需要的等分数量
max_mood - 最短的木头
//binary_searchSelf函数
left,right - 表示搜索范围
middle - 表示搜索范围的中间值
equal_NewNum - 实际等分数量
ans - 二分结果
思路
与 P1873 砍树 还是有些相似点的
首先,根据题目可以得出
- 已知:木头数量,等分数量,每个木头长度
- 求:最大等分长度
这个最大等分长度 肯定是在一定范围内求出来的,这个范围就是**[1, 最大木头的长度]
**
实际上,刚开始写的时候,我的范围是
[1, 最小木头的长度]
,我当时还以为,所有的木头都必须要分至少一个,所以我就将等分长度确定在了1~最小木头的长度
,但实际上,题目中并没有要求每个木头都必须要砍,所以我的想法是不正确的。
确定了范围,就要在这个范围中找到合适的等分长度 了,利用二分搜索法。
二分搜索函数
先确定开始的范围,
left = 0, right = min_tree
循环条件是:
left <= right
每次循环,都要计算
middle = (left + right) / 2
,找到范围中间的值并且计算出,当等分长度为
middle
的时候,所有木头总共可以分成多少段因为每个木头长度可能不同,所以再写一个循环,计算出所有木头按照
middle
的长度分,能分多少段,这就要进行一个累加的操作,我将写一个变量equal_NewNum
存储分段数量然后在计算完分段数量以后,判断是否
equal_NewNum == equal_num
- 如果是 ,则返回
middle
(因为这个分段数量是在等分长度为middle
的时候算的)判断是否
equal_NewNum < equal_num
- 如果是 ,则表示
分段数量 < 所需分段数量
,进而得出分段长度++太长++ 导致 分段数量++过少++ 的结论- 此时就要调整
范围下限left
,left = middle + 1
判断是否
equal_NewNum > equal_num
- 如果是 ,则表示
分段数量 > 所需分段数量
,进而得出分段长度++太短++ 导致 分段数量++过多++ 的结论- 此时就要调整
范围下限right
,left = middle - 1
总结一下
- 确定最大等分长度范围
- 通过二分搜索在范围内找到等分长度,并返回
- 使用二分函数,打印返回结果
其中最重要的地方就是这个二分搜索 如何去写,其中有一些细节或特殊情况要判断进去,否则,过不了某些测试点
不过还有个特殊情况,我也没进行特判
如果连 1cm 长的小段都切不出来,输出
0
。
代码
cpp
// 洛谷P2440木材加工
// 2025-02-18
// 郑龙浩/仟濹(CSDN)
//main函数
// moods - 每个木头长度
// num - 木头数量
// equal_length - 等分的木头长度
// equal_num - 需要的等分数量
// max_mood - 最短的木头
//binary_searchSelf函数
// left,right - 表示搜索范围
// middle - 表示搜索范围的中间值
// equal_NewNum - 实际等分数量
// ans - 二分结果
#include <iostream>
#include <algorithm>
using namespace std;
long long binary_searchSelf( long long moods[], long long num, long long equal_num, long long max_mood ){
long long left = 1, right = max_mood; // 设置范围
long long equal_NewNum = 0; // 实际等分数量
long long middle = 0; // 范围中间值
long long ans = 0;
while( left <= right ){
equal_NewNum = 0; // 每次累加之前都要重置
middle = (left + right) / 2;
// 计算实际等分数量
// 就是计算每个木头按照 middle 可以等分多少段
// 每层循环进行累加
for( int i = 0; i < num; i ++ ){
equal_NewNum += moods[ i ] / middle;
}
if( equal_NewNum >= equal_num ){
ans = middle;
left = middle + 1;
} else {
right = middle - 1;
}
}
return ans;
}
int main( void ){
long long num, equal_num; // 木头数量 分段数量
cin >> num >> equal_num; // 输入木头数量 + 分段数量
long long moods[ num ] = {0}; // 木头数据 -> 每个木头的长度
long long max_mood = 0;
for( int i = 0; i < num; i ++ ){
cin >> moods[ i ];
}
sort( moods, moods + num ); // 升序排序
max_mood = moods[ num - 1 ]; // 最长木头
cout << binary_searchSelf( moods, num, equal_num, max_mood );
return 0;
}