[C++]LeetCode 1760 袋子里最少数目的球
[C++]LeetCode 1760. 袋子里最少数目的球
题目描述
Difficulty: 中等
给你一个整数数组 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 <= 105
- 1 <= maxOperations, nums[i] <= 109
思路
二分答案+贪心
”最大值最小/最小值最大“类问题往往是二分答案+贪心。
注意可以进行如下操作至多 maxOperations
次,可以发现答案具有单调性:
以样例为例,nums = [9], maxOperations = 2
,设单个袋子里球数目的最大值为\(mx\),那么\(mx\)的取值是有范围的:具体来说,\(3\leq mx \leq 9\)时,都可以在maxOperations = 2
次以内找到对应的划分。
例如,(注意这里的划分方法)
\(mx=3\),可以在2次以内划分为[3, 3, 3]
;
\(mx=4\),可以在2次以内划分为[4, 4, 1]
;
\(mx=5\),可以在2次以内(实际只用1次)划分为[5, 4]
;
依次类推,而\(mx=2\)时,在2次以内划分无法满足要求。
于是,由答案的单调性,我们可以将原问题转化为判定问题,进而对答案二分。
二分下界\(l=1\),\(r=\max\{nums_i\}\),以样例为例,首先l = 1, r = 9
,我们检查发现check( mid = 5 )
成立,因此答案在[1, 5]
之间,继续检查发现check( mid = 2 )
不成立,因此答案在[3, 5]
之间,依次类推最后确定答案。
那么,我们该如何判断一个答案\(mx\)是否成立呢?贪心即可。将nums
中每个数贪心地划分至小于等于mx
,最后统计需要的划分次数是否超过maxOperations
。
对nums
中每个数\(nums_i\),要将其划分至不超过\(mx\),需要的最少次数为nums[i] % mx == 0 ? nums[i] / mx - 1 : nums[i] / mx
。举个例子,nums[i] = 9
,当mx = 4
时,因为9 = 2 * 4 + 1
,则最少需要\(\lfloor \frac{9}{4} \rfloor = 2\)次,划分为[4, 4, 1]
。而当mx = 3
时,因为9 = 3 * 3
,则最少需要\(\lfloor \frac{9}{3}\rfloor - 1= 2\)次,划分为[3, 3, 3]
。
本题中,这个写法等价于\(\lfloor \frac{nums[i] - 1}{mx} \rfloor\)。
复杂度:
二分次数\(O(\log M) , M=\max\{nums_i\}\),而每次判断是\(O(n)\)的。
故总复杂度为\(O(n\log M), M=\max\{nums_i\}\)。
Code
Language: C++
class Solution {
inline bool check(int mx, const vector<int>& nums, int maxOperations) {
int cnt = 0;
for (const auto& num : nums) {
cnt += (num - 1) / mx;
if (cnt > maxOperations) return false;
}
return true;
}
public:
int minimumSize(vector<int>& nums, int maxOperations) {
int l = 1, r = 0, ans = 0;
for (const auto &num : nums) r = max(r, num);
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid, nums, maxOperations)) ans = mid, r = mid - 1;
else l = mid + 1;
}
return ans;
}
};