leetcode
leetcode 2401 ~ 2450
长度递增组的最大数目

长度递增组的最大数目

难度:

标签:

题目描述

You are given a 0-indexed array usageLimits of length n.

Your task is to create groups using numbers from 0 to n - 1, ensuring that each number, i, is used no more than usageLimits[i] times in total across all groups. You must also satisfy the following conditions:

  • Each group must consist of distinct numbers, meaning that no duplicate numbers are allowed within a single group.
  • Each group (except the first one) must have a length strictly greater than the previous group.

Return an integer denoting the maximum number of groups you can create while satisfying these conditions.

 

Example 1:

Input: usageLimits = [1,2,5]
Output: 3
Explanation: In this example, we can use 0 at most once, 1 at most twice, and 2 at most five times.
One way of creating the maximum number of groups while satisfying the conditions is: 
Group 1 contains the number [2].
Group 2 contains the numbers [1,2].
Group 3 contains the numbers [0,1,2]. 
It can be shown that the maximum number of groups is 3. 
So, the output is 3. 

Example 2:

Input: usageLimits = [2,1,2]
Output: 2
Explanation: In this example, we can use 0 at most twice, 1 at most once, and 2 at most twice.
One way of creating the maximum number of groups while satisfying the conditions is:
Group 1 contains the number [0].
Group 2 contains the numbers [1,2].
It can be shown that the maximum number of groups is 2.
So, the output is 2. 

Example 3:

Input: usageLimits = [1,1]
Output: 1
Explanation: In this example, we can use both 0 and 1 at most once.
One way of creating the maximum number of groups while satisfying the conditions is:
Group 1 contains the number [0].
It can be shown that the maximum number of groups is 1.
So, the output is 1. 

 

Constraints:

  • 1 <= usageLimits.length <= 105
  • 1 <= usageLimits[i] <= 109

代码结果

运行时间: 151 ms, 内存: 30.7 MB


/*
 * 思路:
 * 使用Java Stream API来简化代码。我们可以通过流操作来计算每个组的大小并更新每个数字的使用次数。
 * 具体步骤:
 * 1. 将数组转换为一个流并降序排序。
 * 2. 逐步增加组的大小,每次从流中取出所需的元素,更新其使用次数。
 * 3. 通过过滤和映射来更新和筛选出可用的数字。
 */

import java.util.Arrays;

public class Solution {
    public int maxGroups(int[] usageLimits) {
        Arrays.sort(usageLimits);
        int n = usageLimits.length;
        int maxGroups = 0;
        int groupSize = 1;

        while (groupSize <= n) {
            int finalGroupSize = groupSize;
            long count = Arrays.stream(usageLimits)
                                .filter(usage -> usage >= finalGroupSize)
                                .count();

            if (count < groupSize) {
                break;
            }

            maxGroups++;
            for (int i = 0; i < n; i++) {
                if (usageLimits[i] >= groupSize) {
                    usageLimits[i] -= groupSize;
                    groupSize++;
                }
            }
        }
        return maxGroups;
    }
}

解释

方法:

该题解采用贪心算法的思路。首先将usageLimits数组排序,这样可以保证我们在构建组的过程中,优先使用使用次数较少的数字。然后,我们使用一个变量remain来记录在构建当前组之前,数组中剩余的总使用次数。变量require用于记录当前组需要达到的最小长度。我们遍历排序后的数组,不断累加每个数字的使用次数到remain中,并检查是否满足当前组的长度要求(即remain >= require)。如果满足,我们就可以构建一个新的组,然后更新remain和require的值,继续构建下一个组。最后,返回构建的组数。

时间复杂度:

O(nlogn)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在这个问题中选择使用贪心算法而不是动态规划或回溯搜索等其他可能的算法?
贪心算法在这个问题中的选用是因为它可以在每一步做出局部最优选择,即优先使用使用次数最少的数字,从而简化问题并提高效率。如果使用动态规划,可能需要构造一个复杂的状态转移方程来处理所有可能的组合,这可能导致时间和空间复杂度显著增加。回溯算法同样会涉及到大量的递归调用和状态回退,可能在处理大数据集时效率不高。因此,贪心算法在这种情况下提供了一个更为高效且实现简洁的解决方案。
🦆
在排序`usageLimits`之后,如何确保每个数字的使用次数不会超过原始`usageLimits`中的限制?
在算法实现中,每个数字的使用次数累加到变量`remain`中,并通过构建新的组来逐步消耗这些累积的使用次数。排序`usageLimits`只是为了优化组的构建过程,确保每次都是使用最小的可用数字。在构建组时,我们从累积的总次数`remain`中减去当前组所需的最小长度`require`,这样就能保证每个数字的使用次数不会超过其在`usageLimits`中的原始限制,因为我们是按照从小到大的顺序使用它们的。
🦆
如何处理`usageLimits`数组中存在大量相同值的情况,这是否会影响最终的组数?
如果`usageLimits`数组中存在大量相同的值,这通常意味着我们可以构建多个长度相同的组,因为我们每次都会尽可能多地使用相同的值来满足当前组的要求。这种情况下,组数可能会受到影响,因为相同值的大量存在可能会允许构建更多的组,尤其是当这些值足够大以支持多个递增长度的组时。总体而言,这会增加可构建的组数,因为相同的高频值提供了更多的灵活性来满足不同组的需求。
🦆
为什么算法中将`remain`减去`require`而不是直接重置`remain`,这样的操作有什么特别的考虑吗?
在算法中,`remain`表示累积的数字使用次数,而`require`是构建下一个组所需的最小长度。通过减去`require`而不是重置`remain`,我们可以保留过多累积的次数并用于构建后续的组。这种做法允许连续的组构建,不浪费任何数字的使用次数,提高了数字利用率。如果直接重置`remain`,则每次构建完一个组后,过剩的数量将被抛弃,这可能导致无法构建更多本可以通过累积次数达成的组。

相关问题