【二分--分堆】 -LeetCode-1011. 在 D 天内送达包裹的能力

1011. 在 D 天内送达包裹的能力

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
提示:

  • 1 < = d a y s < = w e i g h t s . l e n g t h < = 5 ∗ 1 0 4 1 <= days <= weights.length <= 5 * 10^4 1<=days<=weights.length<=5104
  • 1 < = w e i g h t s [ i ] < = 500 1 <= weights[i] <= 500 1<=weights[i]<=500

一开始误以为是“背包问题”,后来看了 三叶姐 题解 才发现原来是 “二分”。

二分

本题和 LeetCode-2226. 每个小孩最多能分到多少糖果 有很大的相似之处。

思路 🤔

  1. 船所需的最低运载能力:为 max,即最起码能保证一次能把 w e i g h t s weights weights中最大的货物运输到另一港口;

  2. 船所需的最高运载能力:为 sum,即一次就把全部货物运输到另一港口;

  3. 开始二分

    利用“两段性”:设最终所需最小装载能力 res。若运载能力 < res 时, 则无法满足 d a y s days days 天运输完所有货物;若运载能力 >= res 时, 则可以满足 d a y s days days 天运输完所有货物

    • 当按照运载能力mid装船时,可以将所有货物都能在 d a y s days days 天内都运输完毕,则“尝试”更小的装载量是否可以满足,即 right = mid - 1
    • 否则,则说明当前 mid 运载能力不足,则需要船具有更大的运载能力,即 left = mid + 1

class Solution {
    public int shipWithinDays(int[] weights, int days) {
        int max = 0;
        int sum = 0;
        for (int weight : weights) {
            max = Math.max(max, weight);
            sum += weight;
        }
        int left = max;  // 最小运载能力
        int right = sum; // 最大运载能力
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (check(weights, mid, days)) { // 按照mid装船时,可以满足,则“尝试”更小的装载量是否可以满足,即mid - 1
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    // 将weight
    boolean check(int[] weights, int target, int days) {
        int cnt = 0;
        for (int i = 0; i < weights.length; ) {
            int sum = 0;
            // 找一堆连续“最接近”(但不超过)target的总和
            while (i < weights.length && sum + weights[i] <= target) {
                sum += weights[i++];
            }
            cnt++;
        }
        return cnt <= days;
    }

    public static void main(String[] args) {
        int[] nums = {1,2,3,4,5,6,7,8,9,10};
        int days = 5;
        System.out.println(new Solution().shipWithinDays(nums, days));
    }
}

在这里插入图片描述

410. 分割数组的最大值

在这里插入图片描述
在这里插入图片描述

二分

class Solution {
    public int splitArray(int[] nums, int m) {
        int n = nums.length;
        int max = 0;
        int sum = 0;
        for (int i : nums) {
            sum += i;
            max = Math.max(max, i);
        }
        int left = max;
        int right = sum;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (check(nums, mid, m)) { // 尝试更小的“分堆和”
                right = mid - 1;
            } else {    
                left = mid + 1;
            }
        }
        return left;
    }

    boolean check(int[] nums, int target, int m) {
        int cnt = 0;
        for (int i = 0; i < nums.length; ) {
            int sum = 0;
            while (i < nums.length && sum + nums[i] <= target) {
                sum += nums[i++];
            }
            cnt++; // 增加“堆数”
        }
        return cnt <= m;
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值