算法题(142):木材加工
审题:
本题需要我们找到可以将木头切割至少k段的单段长度最长值
思路:
方法一:暴力解法首先我们知道单段长度的最长值就是数组中数据的最大值max,所以我们可以遍历1~max的数据,将他们确定为l,然后计算出当前的切割段数,若大于等于k就记录下当前的l给answer变量,当遇到不满足大于等于k的情况,我们就直接退出循环,输出结果
优化1:逆序遍历1~max
由于我们是寻找满足段数大于等于k的最大l,而l越大段数其实越小,也就是说如果我们逆序遍历,段数是逐渐增加的,l是逐渐递减的,若我们遇到段数大于等于k,此时的l就是结果
时间复杂度:O(k*n)
因为我们外层遍历的是数组数据的最大值,而这个最大值最坏的情况是1e8,内层循环需要遍历数组计算段数,最坏情况进行1e5次,所以总共运行次数可能达到1e13,,一定超时
方法二:二分答案查找
其实我们的答案l的区间就是0到1e8(特殊处理了1cm的l也无法切割足够段数的情况,将0加入到答案区间),假设我们的答案为answer,那么answer自身以及其左边区域的l的段数k'都是大于等于k的(因为他们的l小,可划分的段数就多),同理answer右边区域的l的段数就都是小于k的。
此时就体现出这个区间的二段性,我们就可以使用二分查找的方法来提高效率了,而这里是对答案区间进行二分查找,所以又叫二分答案
第一步:二分查找答案区间
判断方法:
(1)当k' >= k:left = mid
(2)当k' < k: right = mid -1
而当left = mid的时候我们需要用向上取整的计算mid方法,防止死循环(出现在left与right相差偶数个数据的情况)
当left = mid + 1的时候用向下取整,防止跳过部分情况(出现在left和right相差奇数个数据的情况)
mid计算方法:(left+right+1)/2
k'计算方法:我们可以采用遍历数组a的数据来累加计算段数的方法
第二步:输出答案
答案就是left,因为答案一定在0到max之间,left和right最后相等就是找到答案了
解题:
#include<iostream> using namespace std; typedef long long ll; const int N = 1e5 + 10; int n, k; int a[N]; //计算l的可切割段数 ll calnum(ll l) {int cnt = 0;for (int i = 1; i <= n; i++){cnt += a[i] / l;}return cnt; } int main() {cin >> n >> k;for (int i = 1; i <= n; i++){cin >> a[i];}int left = 0;int right = 1e8;ll mid = 0;while (left < right){mid = (left + right + 1) / 2;if (calnum(mid) < k){right = mid - 1;}else{left = mid;}}cout << left << endl;return 0; }
P2440 木材加工 - 洛谷