这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战
题目
1011. 在 D 天内送达包裹的能力
传送带上的包裹必须在 days
天内从一个港口运送到另一个港口。
传送带上的第 i
个包裹的重量为 weights[i]
。每一天,我们都会按给出重量(weights
)的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。
返回能在 days
天内将传送带上的所有包裹送达的船的最低运载能力。
示例 1:
输入: 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
请注意,货物必须按照给定的顺序装运,因此使用载重能力为 14 的船舶并将包装分成 (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) 是不允许的。
示例 2:
输入: weights = [3,2,2,4,1,4], days = 3
输出: 6
解释:
船舶最低载重 6 就能够在 3 天内送达所有包裹,如下所示:
第 1 天:3, 2
第 2 天:2, 4
第 3 天:1, 4
示例 3:
输入: weights = [1,2,3,1,1], D = 4
输出: 3
解释:
第 1 天:1
第 2 天:2
第 3 天:3
第 4 天:1, 1
提示:
1 <= days <= weights.length <= 5 * 104
1 <= weights[i] <= 500
思路
- 这道题目也是二分查找的一道经典题目,二分查找不单单应用于找索引元素,在有序链表中来找到某个元素,我们也可以把取值范围界定好左右边界,然后通过二分法来快速找到某个值;
- 首先我们对整个数组进行一次求和操作,得出总数
sum
,然后平均分给days
天,求出来的平均值就是我们的左边界left
,也意味着最好的情况是刚好大家平均分完的次数,最坏的情况也就是所有的元素全部得同一天运送完成,所以我们的右边界right
也就等于总数sum
; - 然后左右边界确定了之后,我们只需要通过二分查找来快速定位到最左边的右边界即可:
- 如果当前二分查找的天数不能完全运输货物,左边界 = 当前值 + 1
- 如果当前二分查找的天数能够完全运输货物,右边界 = 当前值
- 这里要明确右边界不能减一,因为左边不够的时候肯定要往右边查找,右边够了左边不一定还够;
- 然后循环执行到左右边界相遇的时候,这时候我们的坐标就是最左边的右边界了。
实现
/**
* @param {number[]} weights
* @param {number} days
* @return {number}
*/
function shipWithinDays(weights, days) {
const sum = weights.reduce((total, cur) => total += cur);
let left = Math.floor(sum / days),
right = sum;
while (left < right) {
const mid = left + right >> 1;
if (validateTarget(weights, days, mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
// 验证当前值是否能运输所有包裹
function validateTarget(nums, days, target) {
let count = 0;
for (let i = 0; i < nums.length; i++) {
// 如果单一项都运输不了说明不行
if (nums[i] > target) {
return false;
}
// 每天尽可能多放,放不下就留到第二天
if (count + nums[i] <= target) {
count += nums[i];
} else {
// 直到天数不够用了,返回false
if (days > 1) {
days--;
count = nums[i];
} else {
return false;
}
}
}
return true;
}
结果
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。