[C++]LeetCode 1760 袋子里最少数目的球

[C++]LeetCode 1760. 袋子里最少数目的球

题目描述

Difficulty: 中等

Related Topics: 数组, 二分查找

给你一个整数数组 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;
		}
};
posted @ 2022-12-20 00:48  宇興  阅读(21)  评论(0编辑  收藏  举报