leetcode-袋子里最少数目的球

205 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情

题目描述

给你一个整数数组 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

思路

本来是想使用模拟,先对nums排序,然后每次都把最大的数均分,然后把这2个数再插入到有序的数组中,经过maxOperations次操作,剩下数组中的最大值就是我们要求解的结果。
看了别人题解中的思路,感觉更有意思一些:不是直接去求解这个最大值,而是先假设一个最大值,然后看是否可行。
我们记当前要尝试的每个袋子中最大的球数量为n,然后我们去求解让nums变成每个最大值为n的数组,至少需要多少次操作,我们可以这样求解,对于每一个num,如果刚好能整除n,那么需要num/n - 1次,否则,需要num/n 次,累加起来,就是需要的最少操作次数,然后这个次数跟maxOperations去比较,我们就可以知道n是否能符合条件。我们再看这个最小次数的求解公式 f(n) =i=0len1numn1\sum_{i=0}^{len-1}\frac{num}{n}-1显然f(n)和n之间,是反相关,即n越大,f(n)越小,是单调的,所以,我们可以使用二分来枚举n,然后求出f(n)看是否符合条件,我们要求的,就是符合条件的最大的n值。

Java版本代码

class Solution {
    public int minimumSize(int[] nums, int maxOperations) {
        int max = Arrays.stream(nums).max().getAsInt();
        int ans = max;
        int left = 1, right = max;
        while (left <= right) {
            int mid = left + (right - left)/2;
            // 判断袋子中最多有mid个球时,是否能满足在maxOperations次操作内
            int sum = 0;
            for (int num : nums) {
                if (num % mid == 0) {
                    sum += num/mid - 1;
                } else {
                    sum += num/mid;
                }
            }
            if (sum <= maxOperations) {
                ans = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }
}