题解:洛谷 P2440 木材加工
【题目来源】
【题目描述】
木材厂有 \(n\) 根原木,现在想把这些木头切割成 \(k\) 段长度均为 \(l\) 的小段木头(木头有可能有剩余)。
当然,我们希望得到的小段木头越长越好,请求出 \(l\) 的最大值。
木头长度的单位是 \(cm\),原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。
例如有两根原木长度分别为 \(11\) 和 \(21\),要求切割成等长的 \(6\) 段,很明显能切割出来的小段木头长度最长为 \(5\)。
【输入】
第一行是两个正整数 \(n,k\),分别表示原木的数量,需要得到的小段的数量。
接下来 \(n\) 行,每行一个正整数 \(L_i\),表示一根原木的长度。
【输出】
仅一行,即 \(l\) 的最大值。
如果连 \(1cm\) 长的小段都切不出来,输出 0。
【输入样例】
3 7
232
124
456
【输出样例】
114
【核心思想】
-
问题分析:给定 \(n\) 根原木,每根长度为 \(L_i\),需要切割出至少 \(k\) 段长度均为 \(l\) 的小段。目标是求满足条件的最大正整数 \(l\)。若 \(l = 1\) 都无法满足,输出 \(0\)。这是一个整数二分问题,关键在于判断某个切割长度 \(l\) 是否可行(即能否得到 \(\geq k\) 段)。
-
算法选择:
- 整数二分(Binary Search on Answer):在答案域 \([1, \max(L_i)]\) 上进行二分查找,将"求最大值"转化为"判定可行性"
- 可行性判定:给定切割长度 \(mid\),计算所有原木能切出的段数总和 \(\sum_{i=1}^{n} \lfloor L_i / mid \rfloor\),若总和 \(\geq k\) 则 \(mid\) 可行
-
关键步骤:
- 初始化:读取 \(n\)(原木数量)、\(k\)(目标段数)、\(L[1..n]\)(各原木长度)
- 设定搜索范围:左边界 \(l = 0\),右边界 \(r = 10^8\)(或 \(\max(L_i)\))
- 二分判定(当 \(l \leq r\) 且 \(r \neq 0\) 时循环):
- 取中点 \(mid = l + (r - l) / 2\),防止溢出
- 计算可切割段数:\(tmp = \sum_{i=1}^{n} \lfloor L_i / mid \rfloor\)
- 判定与调整:
- 若 \(tmp \geq k\):\(mid\) 可行,尝试更大的长度,令 \(l = mid + 1\)
- 若 \(tmp < k\):\(mid\) 不可行,需要更短的长度,令 \(r = mid - 1\)
- 输出答案:循环结束时 \(r\) 即为满足条件的最大长度(若始终不可行则 \(r = 0\))
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log \max(L_i))\),二分约 \(\log 10^8 \approx 27\) 次,每次判定 \(O(n)\)
- 空间复杂度:\(O(n)\),存储原木长度数组
-
整数二分的核心思想:
- 答案域单调性:切割长度 \(l\) 越大,能得到的段数越少,具有单调递减性,满足二分条件
- 判定与搜索分离:不直接求解,而是构造判定函数 \(check(mid)\) 判断可行性,将最优化问题转化为判定问题
- 边界处理技巧:使用 \(l = mid + 1\) 和 \(r = mid - 1\) 避免死循环,最终答案取 \(r\)(最后一个可行的值)
- 适用场景:求满足条件的最大值/最小值,且条件具有单调性的最优化问题
【解题思路】

【算法标签】
普及 #整数二分
【代码详解】
#include <bits/stdc++.h>
using namespace std;
// 全局变量:
// n: 绳子数量
// k: 需要切割的段数
// a[100005]: 存储每根绳子的长度
int n, k, a[100005];
int main()
{
// 输入绳子数量n和需要切割的段数k
cin >> n >> k;
// 输入每根绳子的长度
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
// 初始化二分查找的左右边界
int l = 0, r = 1e8;
// 二分查找最大可能的切割长度
while (l <= r && r != 0)
{
int tmp = 0; // 记录当前切割长度下能得到的段数
int mid = l + (r - l) / 2; // 计算中间值,防止溢出
// 计算所有绳子按mid长度切割能得到的段数总和
for (int i = 0; i < n; i++)
{
tmp += a[i] / mid;
}
// 根据计算结果调整搜索范围
if (tmp >= k)
{
l = mid + 1; // 可以切割更长,向右搜索
}
else if (tmp < k)
{
r = mid - 1; // 需要切割更短,向左搜索
}
}
// 输出最终确定的最大切割长度
cout << r << endl;
return 0;
}
【运行结果】
3 7
232
124
456
114
浙公网安备 33010602011771号