[LeetCode] 1889. 装包裹的最小浪费空间

166 阅读3分钟

👉 “Offer 驾到,掘友接招!我正在参与2022春招打卡活动点击查看 活动详情

1889. 装包裹的最小浪费空间

给你 n 个包裹,你需要把它们装在箱子里,每个箱子装一个包裹。总共有 m 个供应商提供 不同尺寸 的箱子(每个规格都有无数个箱子)。如果一个包裹的尺寸 小于等于 一个箱子的尺寸,那么这个包裹就可以放入这个箱子之中。

包裹的尺寸用一个整数数组 packages 表示,其中 packages[i] 是第 i 个包裹的尺寸。供应商用二维数组 boxes 表示,其中 boxes[j] 是第个供应商提供的所有箱子尺寸的数组。

你想要选择 一个供应商 并只使用该供应商提供的箱子,使得 总浪费空间最小 。对于每个装了包裹的箱子,我们定义 浪费的 空间等于 箱子的尺寸 - 包裹的尺寸 。总浪费空间 为 所有 箱子中浪费空间的总和。

比方说,如果你想要用尺寸数组为 [4,8] 的箱子装下尺寸为 [2,3,5] 的包裹,你可以将尺寸为 3 的两个包裹装入两个尺寸为的箱子中,同时把尺寸为的包裹装入尺寸为的箱子中。总浪费空间为 (4-2) + (4-3) + (8-5) = 6 。 请你选择 最优 箱子供应商,使得 总浪费空间最小 。如果 无法 将所有包裹放入箱子中,请你返回 -1 。

由于答案可能会 很大 ,请返回它对 109+710^9 + 7 取余 的结果。

示例 1:

输入:packages = [2,3,5], boxes = [[4,8],[2,8]]
输出:6
解释:选择第一个供应商最优,用两个尺寸为 4 的箱子和一个尺寸为 8 的箱子。
总浪费空间为 (4-2) + (4-3) + (8-5) = 6 。
示例 2:

输入:packages = [2,3,5], boxes = [[1,4],[2,3],[3,4]]
输出:-1
解释:没有箱子能装下尺寸为 5 的包裹。
示例 3:

输入:packages = [3,5,8,10,11,12], boxes = [[12],[11,9],[10,5,14]]
输出:9
解释:选择第三个供应商最优,用两个尺寸为 5 的箱子,两个尺寸为 10 的箱子和两个尺寸为 14 的箱子。
总浪费空间为 (5-3) + (5-5) + (10-8) + (10-10) + (14-11) + (14-12) = 9

提示:

n == packages.length 
m == boxes.length 
1 <= n <= 10^5
1 <= m <= 10^5
1 <= packages[i] <= 10^5
1 <= boxes[j].length <= 10^5
1 <= boxes[j][k] <= 10^5
sum(boxes[j].length) <= 10^5
boxes[j] 中的元素 互不相同 。

解题思路:

对于每个供应商提供的箱子,每个包裹需要选择不小于包裹尺寸的最小的箱子。

用示例3的供应商3举例:

packages358101112
box5510101414

这里考虑二分查找,对于每个供应商提供的一组箱子,从小到大遍历箱子的尺寸,然后尽可能放入更多的包裹。如果包裹已经被之前的箱子放入了,那么就不在考虑放到当前箱子里面。

C++的STL库中有个upper_bound,就是找到大于当前值的第一个位置。如果这个值是箱子尺寸,那么找到的位置就是当前箱子放不下的第一个包裹的位置。

这里还需要用前缀和来处理一下包裹的容量和,方便计算

一些情况的处理:
如果供应商提供的箱子中的最大值小于最大包裹的尺寸,就是无法将包裹放进箱子里面。

代码如下:

class Solution {
public:
    int minWastedSpace(vector<int>& packages, vector<vector<int>>& boxes) {
        // 排序包裹
        sort(packages.begin(), packages.end());

        // 包裹容量和前缀
        vector<long long> prefix{0};
        for(auto it: packages){
            prefix.push_back(prefix.back() + it);
        }
        int maxBox = 0;
        long long ret = LONG_LONG_MAX;
        // 遍历每一个供应商
        for(auto& box: boxes){
            // 排序箱子
            sort(box.begin(), box.end());
            if(box.back() < packages.back()){
                continue;
            }
            long long tmp = 0;
            long long prev = 0;
            // 遍历供应商的每一个箱子
            for(int i = 0; i < box.size(); ++i){
                // 找到箱子放不下的包裹的第一个位置
                auto pos = upper_bound(packages.begin(), packages.end(), box[i]) - packages.begin();
                // 用当前箱子的总尺寸 减去 当前箱子放下的包裹的总尺寸
                tmp += 1ll * (pos - prev) * box[i] - 1ll * (prefix[pos] - prefix[prev]); 
                prev = pos;
            }
            ret = min(ret, tmp);
        }
        if(ret == LONG_LONG_MAX){
            return -1;
        }else{
            return ret % 1000000007;
        }
    }
};
  • 时间复杂度 O(boxes×log2(packages))O(boxes \times log_2(packages))
  • 空间复杂度 O(n)O(n)