1889. 装包裹的最小浪费空间
给你 n
个包裹,你需要把它们装在箱子里,每个箱子装一个包裹。总共有 m
个供应商提供 不同尺寸 的箱子(每个规格都有无数个箱子)。如果一个包裹的尺寸 小于等于 一个箱子的尺寸,那么这个包裹就可以放入这个箱子之中。
包裹的尺寸用一个整数数组 packages
表示,其中 packages[i]
是第 i
个包裹的尺寸。供应商用二维数组 boxes
表示,其中 boxes[j]
是第 j
个供应商提供的所有箱子尺寸的数组。
你想要选择 一个供应商 并只使用该供应商提供的箱子,使得 总浪费空间最小 。对于每个装了包裹的箱子,我们定义 浪费的 空间等于 箱子的尺寸 - 包裹的尺寸
。总浪费空间 为 所有 箱子中浪费空间的总和。
[4,8]
的箱子装下尺寸为 [2,3,5]
的包裹,你可以将尺寸为 2
和 3
的两个包裹装入两个尺寸为 4
的箱子中,同时把尺寸为 5
的包裹装入尺寸为 8
的箱子中。总浪费空间为 (4-2) + (4-3) + (8-5) = 6
。请你选择 最优 箱子供应商,使得 总浪费空间最小 。如果 无法 将所有包裹放入箱子中,请你返回 -1
。由于答案可能会 很大 ,请返回它对 109 + 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 <= 105
1 <= m <= 105
1 <= packages[i] <= 105
1 <= boxes[j].length <= 105
1 <= boxes[j][k] <= 105
sum(boxes[j].length) <= 105
boxes[j]
中的元素 互不相同 。原站题解
java 解法, 执行用时: 38 ms, 内存消耗: 67.3 MB, 提交时间: 2023-07-23 11:26:43
class Solution { public int minWastedSpace(int[] packages, int[][] boxes) { int n = packages.length; Arrays.sort(packages); long[] preSum = new long[n + 1]; for (int i = 0; i < n; ++i) { preSum[i + 1] = preSum[i] + packages[i]; } long res = Long.MAX_VALUE; for (int[] box : boxes) { Arrays.sort(box); if (packages[n - 1] > box[box.length - 1]) { continue; } long t = 0; int low = 0; for (int b : box) { int idx = searchRight(packages, b, low); // 这里需要手动转 long t += ((idx - low) * (long) b - (preSum[idx] - preSum[low])); low = idx; } res = Math.min(res, t); } return res == Long.MAX_VALUE ? -1 : (int) (res % 1000000007); } private int searchRight(int[] packages, int target, int low) { int high = packages.length; while (low < high) { int mid = (low + high) >> 1; if (packages[mid] <= target) { low = mid + 1; } else { high = mid; } } return low; } }
cpp 解法, 执行用时: 292 ms, 内存消耗: 113.3 MB, 提交时间: 2023-07-23 11:25:21
class Solution { private: using LL = long long; static constexpr int MOD = 1000000007; public: int minWastedSpace(vector<int>& packages, vector<vector<int>>& boxes) { int n = packages.size(); sort(packages.begin(), packages.end()); // 计算数组 packages 的前缀和 vector<LL> pre(n + 1); for (int i = 1; i <= n; ++i) { pre[i] = pre[i - 1] + packages[i - 1]; } // 辅助函数,通过前缀和数组,得到数组 packages[left..right] 的和 auto get = [&](int left, int right) { return pre[right + 1] - pre[left]; }; LL ans = LLONG_MAX; for (auto& box: boxes) { sort(box.begin(), box.end()); // 小优化,如果最大包裹的尺寸大于最大箱子的尺寸,那么一定不满足,直接跳过 if (packages.back() > box.back()) { continue; } // 初始化指针 pt,它指向还未被放入箱子的第一个包裹 auto pt = packages.begin(); // 总浪费空间 LL total = 0; for (int y: box) { // 小优化,如果当前箱子 y 的尺寸小于 pt 指向的包裹,那么无需进行二分查找 if (y < *pt) { continue; } // pt' auto pt_next = prev(upper_bound(pt, packages.end(), y)); total += (LL)(pt_next - pt + 1) * y - get(pt - packages.begin(), pt_next - packages.begin()); pt = next(pt_next); // 小优化,如果所有包裹都已经被放入箱子,可以提前退出 if (pt == packages.end()) { break; } } ans = min(ans, total); } return (ans == LLONG_MAX ? -1 : ans % MOD); } };
python3 解法, 执行用时: 316 ms, 内存消耗: 36.1 MB, 提交时间: 2023-07-23 11:24:53
class Solution: def minWastedSpace(self, packages: List[int], boxes: List[List[int]]) -> int: MOD = 10**9 + 7 packages.sort() # 计算数组 packages 的前缀和 pre = list(accumulate(packages, initial=0)) # 辅助函数,通过前缀和数组,得到数组 packages[left..right] 的和 get = lambda left, right: pre[right + 1] - pre[left] ans = float("inf") for box in boxes: box.sort() # 小优化,如果最大包裹的尺寸大于最大箱子的尺寸,那么一定不满足,直接跳过 if packages[-1] > box[-1]: continue # 初始化指针 pt,它指向还未被放入箱子的第一个包裹 pt = 0 # 总浪费空间 total = 0 for y in box: # 小优化,如果当前箱子 y 的尺寸小于 pt 指向的包裹,那么无需进行二分查找 if y < packages[pt]: continue # pt' pt_next = bisect_right(packages, y, pt) - 1 total += (pt_next - pt + 1) * y - get(pt, pt_next) pt = pt_next + 1 # 小优化,如果所有包裹都已经被放入箱子,可以提前退出 if pt == len(packages): break ans = min(ans, total) return -1 if ans == float("inf") else ans % MOD
golang 解法, 执行用时: 248 ms, 内存消耗: 13.2 MB, 提交时间: 2023-07-23 11:23:28
// 由于包裹的尺寸之和是固定的,因此目标转换为最小化箱子的尺寸之和。 // 将包裹按尺寸排序后,按照箱子尺寸划分包裹,使得每个包裹都装入能装该包裹的最小的箱子。 // 这种贪心策略能使箱子的尺寸总和尽可能地小。 func minWastedSpace(a []int, boxes [][]int) int { sort.Ints(a) // 将包裹按尺寸排序,方便后面按照箱子尺寸划分包裹 ans := math.MaxInt64 for _, box := range boxes { sort.Ints(box) if box[len(box)-1] < a[len(a)-1] { // 最大的箱子不够装最大的包裹 continue } res, l := 0, 0 for _, v := range box { // 划分包裹:当前箱子 v 可以装入下标在 [l, r) 区间内的包裹 r := sort.SearchInts(a, v+1) res += (r - l) * v // 统计箱子尺寸之和 l = r } ans = min(ans, res) } if ans < math.MaxInt64 { for _, v := range a { ans -= v // 减去每个包裹尺寸 } return ans % (1e9 + 7) } return -1 } func min(a, b int) int { if a < b { return a } return b }