原题链接:P2440 木材加工 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
# 木材加工
## 题目背景
要保护环境
## 题目描述
木材厂有 $n$ 根原木,现在想把这些木头切割成 $k$ 段长度**均**为 $l$ 的小段木头(木头有可能有剩余)。
当然,我们希望得到的小段木头越长越好,请求出 $l$ 的最大值。
木头长度的单位是 $\text{cm}$,原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。
例如有两根原木长度分别为 $11$ 和 $21$,要求切割成等长的 $6$ 段,很明显能切割出来的小段木头长度最长为 $5$。
## 输入格式
第一行是两个正整数 $n,k$,分别表示原木的数量,需要得到的小段的数量。
接下来 $n$ 行,每行一个正整数 $L_i$,表示一根原木的长度。
## 输出格式
仅一行,即 $l$ 的最大值。
如果连 $\text{1cm}$ 长的小段都切不出来,输出 `0`。
## 样例 #1
### 样例输入 #1
```
3 7
232
124
456
```
### 样例输出 #1
```
114
```
## 提示
#### 数据规模与约定
对于 $100\%$ 的数据,有 $1\le n\le 10^5$,$1\le k\le 10^8$,$1\le L_i\le 10^8(i\in[1,n])$。
思路:
比较明显的二分,二分的关键便是确定要查找的对象,找到合适的边界和check函数。
此题中查找的对象便是每一小段的长度,所以不难确定每一小段的长度范围0~所有木头总长/k。
现在写出check函数即可,题目的要求是l尽可能大,且总段数=k。所以check函数用来判断此段长下的段数是否>=k,若>=k,则说明l还可以更长。并且是要求l的最大值,因为区间显然是单增的,显然为true时,是在mid的右半边不断二分。从而找到最右边的最大值。所以更新区间时是l=mid。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N];
typedef long long ll;
int n, k,R,L;
ll t;
bool check(int x) {//check函数用来检查是否能满足>=k的要求
int cnt = 0; //显然若>=k,说明l还可以更大。最后查找的边界便是题目的解。
for (int i = 0; i < n; i++) {
cnt += a[i] / x;
}
if (cnt>=k)return true;
else return false;
}
void bs(int l, int r) {
while (l < r) {
int mid = l + r +1>> 1;//向上取整避免死循环
if (check(mid))l = mid;
//既然是求l的最大值,显然是不断的左边界右移动,右边界左移。
//显然区间单增,得到的便是最右边的最大值。
else r = mid -1;
}
L = l;
}
int main(){
cin >> n >> k;
for (int i = 0; i < n; i++) {
cin >> a[i];
t += a[i];
}
R = t / k;//长度l的右边界,向下取整
bs(0, R);
cout << L;
return 0;
}