木材加工 (二分答案)

原题链接: 洛谷 p2440 木材加工

题面

木材厂有 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, 比较 sumk 的大小, 若 sum >= k 即 md 可能是答案, 也可能 md 的值有点小. 这时, 我们要取比 md 大的数再遍历一遍, 找到 sum 与 k 进行比较, 即我们让左值 left = md + 1; 同理若 sum < k 则 md 值过大取不到结果, 我们令 right = md - 1, 令 md 变小.

以下是我写的代码:

#include //洛谷 p2440 木材加工
#include 
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;
}

你可能感兴趣的:(题解)