开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第23天,点击查看活动详情
✨欢迎关注🖱点赞🎀收藏⭐留言✒
🔮本文由京与旧铺原创,csdn首发!
😘系列专栏:java学习
💻首发时间:🎞2022年12月10日🎠
🀄如果觉得博主的文章还不错的话,请三连支持一下博主哦
🎧作者是一个新人,在很多方面还做的不好,欢迎大佬指正,一起学习哦,冲冲冲
⭐️1760. 袋子里最少数目的球⭐️
🔐题目详情
题目链接: 1760. 袋子里最少数目的球 难度中等
给你一个整数数组 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^51 <= maxOperations, nums[i] <= 10^9
💡解题思路
解题思路:
这道题给我们maxOperations 次操作机会,将袋子里面的求进行分组,就是每次操作会将一个袋子里面的求分成两个部分,装进两个新的袋子里面,当操作完毕后,求最小的开销,开销为所有袋子中最大球的个数。
按照题意直接暴力模拟,好像也不太好模拟,但是我们可以换一种思路,它不是让我们求最小的开销嘛,那我们就枚举开销mid,在满足maxOperations 次数限制的条件下,求最小的开销值。
当开销为mid时,所需的操作次数要怎么求呢?我们来找一找规律:
- 对于小球个数在[1, mid]区间的袋子,不需要进行分组,操作次数为
0。 - 对于小球个数在[mid + 1, 2 * mid]区间的袋子,需要分组一次,操作次数为
1。 - 对于小球个数在[2 * mid + 1, 3 * mid]区间的袋子,需要分组两次,操作次数为
2。
诶嘿!是不是有点感觉了呢?
- 对于小球个数在[(n - 1) * mid + 1, n * mid]区间的袋子,需要分组n - 1次,操作次数为
n - 1。 - 对于小球个数在[n * mid + 1, (n + 1) * mid]区间的袋子,需要分组n次,操作次数为
n。
根据规律,可以参考右边界与操作次数的关系,得出对于小球个数为x的袋子,需要操作次。
本题可以考虑二分枚举,因为对于开销与操作次数的关系具有二段性:
- 当操作次数大于
maxOperations时,最小的开销值一定比mid大,在mid右边。 - 当操作次数小于或等于
maxOperations时,最小的开销一定不大于mid,要么在mid左边,要么就是mid。
对于开销的大小,左边界为1,右边界为题目给定元素最大值1e9。
🔑源代码
java实现代码:
class Solution {
public int minimumSize(int[] nums, int maxOperations) {
int n = nums.length;
int l = 1;
int r = (int) 1e9 + 1;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid, nums, maxOperations)) r = mid;
else l = mid + 1;
}
return l;
}
//计算总操作次数并验证
private boolean check(int mid, int[] nums, int maxOperations) {
//计算总操作次数
long s = 0;
for (int x : nums) {
s += (x - 1) / mid;
}
return s <= maxOperations;
}
}
c++实现代码:
class Solution {
public:
int minimumSize(vector<int>& nums, int maxOperations) {
int l = 1;
int r = (int) 1e9 + 1;
while (l < r)
{
int mid = (l + r) >> 1;
if (check(mid, nums, maxOperations)) r = mid;
else l = mid + 1;
}
return l;
}
bool check(int& mid, vector<int>& nums, int& maxOperations)
{
long s = 0;
for (int x : nums)
{
s += (x - 1) / mid;
}
return s <= maxOperations;
}
};
c实现代码:
bool check(int mid, int* nums, int numsSize,int maxOperations)
{
long s = 0;
for (int i = 0; i < numsSize; i++) s += (nums[i] - 1) / mid;
return s <= maxOperations;
}
int minimumSize(int* nums, int numsSize, int maxOperations){
int l = 1;
int r = (int) 1e9 + 1;
while (l < r)
{
int mid = (l + r) >> 1;
if (check(mid, nums, numsSize, maxOperations)) r = mid;
else l = mid + 1;
}
return l;
}
🌱总结
本题为二分枚举运用题,难点在于问题的转换。