算法题-N趟最低运载

该文源自便利蜂一面时的算法题,当时没能给出比较优雅的解法。
事后复盘,并花时间对该题重新作答,在此记录下来。

一、算法题目

每天夜里便利蜂司机需要从仓库把装箱的包裹配送到门店。包裹重量不一,【必须按照出仓顺序】装车。

司机每跑一趟就能赚一趟的运费,但是最多不能超过要求的 N 趟, 请帮司机选择一辆【最低运载能力】的卡车来赚到更多运费。

例如:

顺序出仓的10个包裹重量依次为 [1,2,3,4,5,6,7,8,9,10] (单位吨), 需要在 5 趟内完成配送。 则需要选择一辆 15 吨的卡车能够刚好跑满 5 趟,赚到 5 份运费。

控制台输入格式: 1,2,3,4,5,6,7,8,9,10#5
输出:15

注意:趟数必须小于等于包裹个数,不能空车跑。

二、最初思路

假设每车刚好装满,则单车载重 = 总重 / 目标趟数,但包裹重量不一且顺序装车,故刚好装满概率极低,需要以单车均重为下限,朝上进行逼近。当时没想到优雅的逼近思路,故采用单车载重逐次+1的暴力逼近。

思路不佳,就不贴实现了。。。

三、复盘思路

a)题目要素

输入:
包裹重量数组;

条件:
装车顺序确定;
最大趟数确定;

输出:
满足要求的单车最低载重;

b)载重区间

单车载重区间的左边界应为 max(均重,最大重量),由于最大重量 >= 均重,故左边界直接取最大重量即可,右边界应为 sum(包裹重量)。

故得 最大重量 <= 单车载重 < 总重,左闭右开区间。

c)逼近策略

有了上下限,则可用二分法进行逼近。

每次取载重区间中点判定趟数是否满足要求:

  • 若判定成功,则舍弃右区间,在左区间继续逼近;
  • 若判定失败,则舍弃左区间,在右区间继续逼近;

d)趟数判定

按包裹顺序遍历,累加当车总重,若当车总重 > 单车负载,则该包裹应放到下一车,即当车总重 = 当前包裹重量,趟数 + 1。

  • 若超过最大趟数,判定为失败;
  • 否则,判定成功;

e)非法检查

职责上来说,该算法聚焦计算最低运载能力即可,输入的合法性检查应交由调用方。

四、实现代码

public int minCarLoad(int[] weights, int n){
    // 载重区间:[最大重量, 总重量)
    int min = 0, max = 0;
    for (int weight : weights){
        min = Math.max(weight, min);
        max += weight;
    }
    while (min < max){
        int mid = min + max >> 1;
        // 当前趟次,该趟总重
        int times = 1, sum = 0;
        boolean flag = true;
        // 趟数判定
        for (int weight : weights){
            if ((sum += weight) > mid){
                if (++times > n){
                    flag = false;
                    break;
                }
                sum = weight;
            }
        }
        // 二分逼近
        if (flag){
            max = mid;
        } else {
            min = mid + 1;
        }
    }
    return max;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值