题目:
给你一个整数数组 nums
,其中 nums[i]
表示第 i
个袋子里球的数目。同时给你一个整数 maxOperations
。
你可以进行如下操作至多 maxOperations
次:
- 选择任意一个袋子,并将袋子里的球分到 2 个新的袋子中,每个袋子里都有 正整数 个球。
- 比方说,一个袋子里有
5
个球,你可以把它们分到两个新袋子里,分别有1
个和4
个球,或者分别有2
个和3
个球。
- 比方说,一个袋子里有
你的开销是单个袋子里球数目的 最大值 ,你想要 最小化 开销。
请你返回进行上述操作后的最小开销。
示例 1:
输入:nums = [9], maxOperations = 2 输出:3 解释: - 将装有 9 个球的袋子分成装有 6 个和 3 个球的袋子。[9] -> [6,3] 。 - 将装有 6 个球的袋子分成装有 3 个和 3 个球的袋子。[6,3] -> [3,3,3] 。 装有最多球的袋子里装有 3 个球,所以开销为 3 并返回 3 。
示例 2:
输入: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 。
示例 3:
输入:nums = [7,17], maxOperations = 2 输出:7
提示:
1 <= nums.length <= 10^5
1 <= maxOperations, nums[i] <= 10^9
解法:二分查找
-
问题分析:
-
我们有一个整数数组
nums
,表示每个袋子中的球的数量。 -
我们可以进行最多
maxOperations
次操作,每次操作可以将一个袋子中的球分成两个新袋子,每个新袋子中的球数必须为正整数。 -
我们的目标是使所有袋子中球的最大值最小化。
-
-
二分查找思路:
-
我们可以通过二分查找来确定最小的最大开销。
-
我们设定一个范围,最小值为 1,最大值为数组中的最大值。
-
对于每个中间值
mid
,我们计算将所有袋子中的球数减少到mid
或更少需要多少次操作。 -
如果需要的操作次数小于等于
maxOperations
,则说明我们可以尝试更小的mid
,否则我们需要增大mid
。
-
-
计算操作次数:
- 对于每个袋子中的球数
num
,将其减少到mid
或更少需要的操作次数为(num - 1) / mid
。-
我们将所有袋子需要的操作次数相加,得到总操作次数。
-
- 对于每个袋子中的球数
-
实现代码:
class Solution {
public:
int minimumSize(vector<int>& nums, int maxOperations) {
int left = 1;
int right = *max_element(nums.begin(), nums.end());
while (left < right) {
int mid = left + (right - left) / 2;
int operations = 0;
for (int num : nums) {
operations += (num - 1) / mid;
}
if (operations <= maxOperations) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
};
代码解释:
-
left
和right
分别表示二分查找的左右边界,初始时left = 1
,right
为数组中的最大值。 -
在每次循环中,我们计算中间值
mid
,并计算将所有袋子中的球数减少到mid
或更少需要的操作次数。 -
如果操作次数小于等于
maxOperations
,则说明我们可以尝试更小的mid
,因此将right
更新为mid
。 -
否则,我们需要增大
mid
,因此将left
更新为mid + 1
。 -
最终,
left
就是我们要求的最小最大开销。
这个方法的时间复杂度是 O(n log m)
,其中 n
是数组的长度,m
是数组中的最大值。