题目重点:用最大仓库频次与每次两件的容量建立双下界,证明 max(最高频次, ceil(n/2)) 可以实现。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 可任意挑选剩余物品,没有输入顺序限制;一次最多两件,不同仓库才可配对。
- 仓库 ID 只表示来源,相同 ID 的物品不因此合并成一件。
- 空输入训练合同返回 0,题目未明确 n 上界。
解题路线
- 令 n 为件数,M 为某仓最大件数。一次最多发两件,至少 ceil(n/2) 次;一次最多发该最大仓的一件,又至少 M 次。
- 若 M>n/2,把其他所有 n−M 件分别配给最大仓,剩余最大仓单发,总操作 (n−M)+(2M−n)=M。
- 若 M≤n/2,可以持续取剩余数量最大的两个不同仓配对,使最大频次不超过剩余件数的半数向上取整;偶数时可完全配对,奇数时只剩一件单发,总 ceil(n/2)。构造时可用频次最大堆,公式求数量则无需堆。
- 扫描 Counter 取最大频次,返回两个下界的最大值。不要因为可以选择不同来源就贪心地按输入相邻项配对。
手动走一遍
训练例 [A,A,A,B,C]:M=3、ceil(5/2)=3。发 (A,B)、(A,C)、A 共 3 次。例 [A,A,B,B,C,C] 可发 (A,B)、(A,C)、(B,C),也是 3 次。全来自 A 则必须 n 次。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
from collections import Counter
def minimum_shipments(warehouses: list[int]) -> int:
counts = Counter(warehouses)
return max(max(counts.values(), default=0), (len(warehouses) + 1) // 2)
复杂度
O(n) 时间、O(u) 空间,u 为不同仓数。只返回最少次数;输出实际配对可用 O(n log u) 的堆构造。独立递归 oracle 枚举单发或所有合法异仓配对,覆盖 3,280 个数组。
易错点与追问
容易失分的边界
- 只返回 ceil(n/2),忽略单个仓占多数。
- 把同仓两件一起发。
- 按相邻位置而非任意剩余件配对。
- Counter 去重后的仓数不能替代物品总数。
继续深挖
- 每次最多三件且必须来自不同仓,双下界是否仍足够?
- 要求 FIFO 或有配对禁忌时为什么公式不再适用?
- 如何输出具体发货清单并验证没有遗漏?
