Medium
题目描述
给你一个整数 n ,表示有 n 家专门的零售商店。有 m 种不同数量的产品,用一个 0 下标 的整数数组 quantities 给出,其中 quantities[i] 表示第 i 种产品的数量。
你需要将所有产品分配给零售商店,需要遵循这些规则:
- 一家商店 至多 只能分得 一种产品 ,但可以分得该种产品的 任意数量 。
- 分配后,每家商店都会被分配一定数量的产品(可能为 0)。设
x为分配给任意商店的最多产品数量,你希望x尽可能小。也就是说,你想 最小化 分配给任意商店的最多商品数量。
返回最小的可能的 x 。
示例 1:
输入:n = 6, quantities = [11,6]
输出:3
解释:一种最优分配方式如下:
- 类型为 0 的 11 件产品被分配到前 4 家商店,数量为:2, 3, 3, 3
- 类型为 1 的 6 件产品被分配到另外 2 家商店,数量为:3, 3
分配给任意商店的最多产品数量为 max(2, 3, 3, 3, 3, 3) = 3 。
示例 2:
输入:n = 7, quantities = [15,10,10]
输出:5
解释:一种最优分配方式如下:
- 类型为 0 的 15 件产品被分配到前 3 家商店,数量为:5, 5, 5
- 类型为 1 的 10 件产品被分配到接下来 2 家商店,数量为:5, 5
- 类型为 2 的 10 件产品被分配到最后 2 家商店,数量为:5, 5
分配给任意商店的最多产品数量为 max(5, 5, 5, 5, 5, 5, 5) = 5 。
示例 3:
输入:n = 1, quantities = [100000]
输出:100000
解释:唯一最优分配方式如下:
- 类型为 0 的 100000 件产品被分配到唯一的商店。
分配给任意商店的最多产品数量为 max(100000) = 100000 。
提示:
m == quantities.length1 <= m <= n <= 10^51 <= quantities[i] <= 10^5
解题思路
这是一道经典的二分查找 + 贪心问题。我们需要找到使得所有商品都能成功分配的最小的最大值。
核心思路
题目具有单调性:如果我们能以最大值 k 分配所有商品,那么对于任何 k' > k 也一定能成功分配。因此可以使用二分查找来寻找最小的可行 k。
算法步骤
确定二分查找的范围:
- 下界:1(最少每家店分配1件商品)
- 上界:
max(quantities)(最坏情况下某类商品全部给一家店)
实现判断函数
canDistribute(k):- 遍历每种商品类型
- 对于数量为
q的商品,如果每家店最多分配k件,需要ceil(q/k)家店 - 所有商品类型需要的店铺总数不能超过
n
二分查找最小值:
- 如果当前值
mid可行,尝试更小的值(right = mid) - 如果不可行,必须增大值(
left = mid + 1)
- 如果当前值
关键点
- 使用
(q + k - 1) / k计算向上取整,避免浮点运算 - 贪心策略:每种商品尽可能平均分配给多家店铺
- 时间复杂度:O(m * log(max_quantity)),其中 m 为商品种类数
代码实现
class Solution {
public:
int minimizedMaximum(int n, vector<int>& quantities) {
int left = 1, right = *max_element(quantities.begin(), quantities.end());
while (left < right) {
int mid = left + (right - left) / 2;
if (canDistribute(n, quantities, mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
private:
bool canDistribute(int n, vector<int>& quantities, int k) {
int stores = 0;
for (int q : quantities) {
stores += (q + k - 1) / k; // 向上取整
if (stores > n) return false;
}
return true;
}
};
class Solution:
def minimizedMaximum(self, n: int, quantities: List[int]) -> int:
def canDistribute(k):
stores = 0
for q in quantities:
stores += (q + k - 1) // k # 向上取整
if stores > n:
return False
return True
left, right = 1, max(quantities)
while left < right:
mid = (left + right) // 2
if canDistribute(mid):
right = mid
else:
left = mid + 1
return left
public class Solution {
public int MinimizedMaximum(int n, int[] quantities) {
int left = 1, right = quantities.Max();
while (left < right) {
int mid = left + (right - left) / 2;
if (CanDistribute(n, quantities, mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
private bool CanDistribute(int n, int[] quantities, int k) {
int stores = 0;
foreach (int q in quantities) {
stores += (q + k - 1) / k; // 向上取整
if (stores > n) return false;
}
return true;
}
}
var minimizedMaximum = function(n, quantities) {
function canDistribute(k) {
let stores = 0;
for (let q of quantities) {
stores += Math.ceil(q / k);
if (stores > n) return false;
}
return true;
}
let left = 1, right = Math.max(...quantities);
while (left < right) {
let mid = Math.floor((left + right) / 2);
if (canDistribute(mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
};
复杂度分析
| 复杂度 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 时间 | O(m × log(max_quantity)) | O(1) |
| 空间 | O(1) | O(1) |
其中 m 是商品种类数,max_quantity 是商品数量的最大值。二分查找需要 log(max_quantity) 次迭代,每次迭代需要 O(m) 时间来检查是否可以分配。
相关题目
. Koko Eating Bananas (Medium)
. Magnetic Force Between Two Balls (Medium)
. Minimum Limit of Balls in a Bag (Medium)
. Minimum Time to Complete Trips (Medium)