题目描述
给你一个整数数组 nums
,其中 nums[i]
表示第 i
个袋子里球的数目。同时给你一个整数 maxOperations
。
你可以进行如下操作至多 maxOperations
次:
- 选择任意一个袋子,并将袋子里的球分到
2
个新的袋子中,每个袋子里都有 正整数 个球。- 比方说,一个袋子里有
5
个球,你可以把它们分到两个新袋子里,分别有1
个和4
个球,或者分别有2
个和3
个球。
- 比方说,一个袋子里有
你的开销是单个袋子里球数目的 最大值,你想要 最小化 开销。
请你返回进行上述操作后的最小开销。
样例
输入:nums = [9], maxOperations = 2
输出:3
解释:
- 将装有 9 个球的袋子分成装有 6 个和 3 个球的袋子。[9] -> [6,3]。
- 将装有 6 个球的袋子分成装有 3 个和 3 个球的袋子。[6,3] -> [3,3,3]。
装有最多球的袋子里装有 3 个球,所以开销为 3 并返回 3。
输入:nums = [2,4,8,2], maxOperations = 4
输出:2
解释:
- 将装有 8 个球的袋子分成装有 4 个和 4 个球的袋子。[2,4,8,2] -> [2,4,4,4,2]。
- 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,4,4,4,2] -> [2,2,2,4,4,2]。
- 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,2,2,4,4,2] -> [2,2,2,2,2,4,2]。
- 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,2,2,2,2,4,2] -> [2,2,2,2,2,2,2,2]。
装有最多球的袋子里装有 2 个球,所以开销为 2 并返回 2。
输入:nums = [7,17], maxOperations = 2
输出:7
限制
1 <= nums.length <= 10^5
1 <= maxOperations, nums[i] <= 10^9
算法
(二分答案) $O(n \log M)$
- 求最大值最小的问题,可以采用二分答案的方式,将原问题转化为判定为问题,然后根据答案的单调性找到最优解。
- 二分开销的最大值。对于开销为 $mid$ 时,通过公式 $(x - 1) / mid$ 下取整来计算 $x$ 个球的袋子需要分解的次数。
时间复杂度
- 设 $M$ 为答案的可能的最大值,每次判定只需要 $O(n)$ 的时间,故总时间复杂度为 $O(n \log M)$。
空间复杂度
- 仅需要常数的额外空间。
C++ 代码
#define LL long long
class Solution {
private:
bool check(const vector<int> &nums, int mid, int maxOperations) {
LL cnt = 0;
for (int x : nums) {
if (x <= mid) continue;
cnt += (x - 1) / mid;
if (cnt > maxOperations)
return false;
}
return true;
}
public:
int minimumSize(vector<int>& nums, int maxOperations) {
int l = 1, r = 1000000000;
while (l < r) {
int mid = (l + r) >> 1;
if (check(nums, mid, maxOperations)) r = mid;
else l = mid + 1;
}
return l;
}
};