题面
木材厂有 n 原木,现在想把这些木头切割成 k 块长度相同的小段木头(木头有可能有剩余),需要得到的小段的数目是给定的。当然,我们希望得到的小段木头越长越好,你的任务是计算能够得到的小段木头的最大长度。木头长度的单位是cm。原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。
例如有两根原木长度分别为11和21,要求切割成到等长的6段,很明显能切割出来的小段木头长度最长为5.
解析
这道题是一道二分答案的题, 对于这道题我们可以先找到原先木头中最长出头的数值 ma, 然后令右值 right = ma, left = 0; 这样 md 就等于 (lf + ri) / 2; 然后遍历所有木头, 找到比 md 长的木头 a[i] , a[i] / md 就是长为 a[i] 的木块可以截取长为 md 木块的个数, 统计所有长度大于 md 的木头可以截取长为 md 木头的个数 sum, 比较 sum 和 k 的大小, 若 sum >= k 即 md 可能是答案, 也可能 md 的值有点小. 这时, 我们要取比 md 大的数再遍历一遍, 找到 sum 与 k 进行比较, 即我们让左值 left = md + 1; 同理若 sum < k 则 md 值过大取不到结果, 我们令 right = md - 1, 令 md 变小.
以下是我写的代码:
#include <iostream>//洛谷 p2440 木材加工
#include <algorithm>
using namespace std;
int a[100010], n, k, ans, ma;
//a[i]用来记录第 i 块木头长度, ma 是最长的木头的长度, ans 最终要求的结果;
bool cmp(int a, int b) {
return a > b;
}
int main() {
cin >> n >> k;
for (int i = 0; i < n; i++) {
cin >> a[i];
ma = max(ma, a[i]);//找到最长的木头长度;
}
sort(a, a + n, cmp);//将木头按长度由长到短排列;
int lf, ri, md, sum;
lf = 0;
ri = ma;//令 ri 初始值为 ma, 因为所有木头长度都小于等于 ma;
//lf 长度左值, ri 长度右值, md 中间长度值;
// sum 获得的 md 长度木块的数量;
while (lf <= ri) {
sum = 0;
md = lf + (ri - lf) / 2;
if (md == 0) {
//md == 0 即 md 大于 1 不能得到 sum 个长
//为 md 的木块,使 sum == k, 返回结果 ans == 0;
ans = 0;
break;
}
for (int i = 0; i < n; i++) {// 遍历所有木块找到比 md 长度小的木块;
if (a[i] >= md) {
sum += a[i] / md;
}
else {
//如果长度小于 md, 即结束循环, 因为数组 a 已是下降序列, 之后的数也小于 md;
break;
}
}
if (sum >= k) { //此时证明 md 数值小于或等于我们求的最终结果;
ans = md;//先令结果 ans == md 以作进一步判断;
lf = md + 1;
}
else {//证明 md 数值过大求不出结果;
ri = md - 1;
}
}
cout << ans << endl;
return 0;
}