1760.袋子里最少数目的球

157 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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^5
  • 1 <= 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的袋子,需要操作(x1)/mid(x-1)/mid次。

本题可以考虑二分枚举,因为对于开销与操作次数的关系具有二段性:

  • 当操作次数大于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;
}

🌱总结

本题为二分枚举运用题,难点在于问题的转换。