[1011. 在 D 天内送达包裹的能力]
「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
题目描述
传送带上的包裹必须在 D 天内从一个港口运送到另一个港口。
传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。
返回能在 D 天内将传送带上的所有包裹送达的船的最低运载能力。
示例
输入:weights = [1,2,3,4,5,6,7,8,9,10], days = 5
输出:15
解释:
船舶最低载重 15 就能够在 5 天内送达所有包裹,如下所示:
第 1 天:1, 2, 3, 4, 5
第 2 天:6, 7
第 3 天:8
第 4 天:9
第 5 天:10
思路
按照题目要求,需要的结果是能在 D 天内将传送带上的所有包裹送达的船的最低运载能力。求的是运载能力,即最在要求的时间内,船的运载量最小。这里涉及到两个变量,运载天数和船的运载量,题目已经给出了运载天数,那么可以从船的运载量开始思考。
那么,可以先想出在忽略天数的情况下考虑运载能力的范围。那么首先最小运载能力应该大于物品的最大值,才有可能完成任务,此时的天数为最大值,即货物的数量(一天运一件),最大运载能力应为物品重量的总和,这样就可以一天运走,再大没有必要,此时的天数的最小值为1天。可以发现运载量都可以对应到一个天数。
之后可以使用二分查找,计算当前运载量运输所需要的最小天数,不断的逼近D。得解。
代码实现
具体到代码实现,按照上述思路,使用二分查找时,我们需要左右界和判断条件。判断条件我们可以使用一个判断函数来实现,具体到这道题,就是判断,给定的货物,天数,运载量,能不能运走。这道题,左右界应为运载量的范围,左界为货物最大值,右界为货物之和。在进行二分查找时,会有多个运载量符合D天的要求,在这些值里面,只有最小的才符合要求。具体实现和注释见下面代码。
class Solution {
private:
bool cantCarry(vector<int>weights,int D,int load){
int curDay=1,curload=0; // 判断在运载能力确定时,能不能在D内运完。
for(int w:weights){
if(curload+w>load){ // 加上这件物品超载,说明当天运量到上限,需要当天发
curload=0; // 清空
++curDay; //天数加1
}
curload+=w; // 装货
}
return curDay>D; // 求出运送的实际天数,判断给定的天数是不是运不完。
}
public:
int shipWithinDays(vector<int>& weights, int D) {
int left= INT_MIN,right=0;
for(int w:weights){
left=max(left,w);
right+=w; //找出左右界
}
while(left<right){ // 二分查找,此处找的是符合条件的最小值
int middle=left+(right-left)/2;
if(cantCarry(weights,D,middle)){ //运不走,移动左,使变大,增加运载量
left=middle+1;
}
else{ // 运得走,移动右,使变小,逼近最小值
right=middle;
}
}
return left; //循环结束时,执行的是left=middle+1,处于有效区间内。
}
};