leetcode
leetcode 2451 ~ 2500
让所有学生保持开心的分组方法数

让所有学生保持开心的分组方法数

难度:

标签:

题目描述

You are given a 0-indexed integer array nums of length n where n is the total number of students in the class. The class teacher tries to select a group of students so that all the students remain happy.

The ith student will become happy if one of these two conditions is met:

  • The student is selected and the total number of selected students is strictly greater than nums[i].
  • The student is not selected and the total number of selected students is strictly less than nums[i].

Return the number of ways to select a group of students so that everyone remains happy.

 

Example 1:

Input: nums = [1,1]
Output: 2
Explanation: 
The two possible ways are:
The class teacher selects no student.
The class teacher selects both students to form the group. 
If the class teacher selects just one student to form a group then the both students will not be happy. Therefore, there are only two possible ways.

Example 2:

Input: nums = [6,0,3,3,6,7,2,7]
Output: 3
Explanation: 
The three possible ways are:
The class teacher selects the student with index = 1 to form the group.
The class teacher selects the students with index = 1, 2, 3, 6 to form the group.
The class teacher selects all the students to form the group.

 

Constraints:

  • 1 <= nums.length <= 105
  • 0 <= nums[i] < nums.length

代码结果

运行时间: 77 ms, 内存: 25.6 MB


/*
 * 思路:
 * 1. 首先将数组 nums 进行排序。
 * 2. 使用 Java Stream 对数组进行处理。
 * 3. 计算有多少学生会保持开心,分为两种情况:
 *    a. 学生被选中且选中的学生人数大于 nums[i]。
 *    b. 学生未被选中且选中的学生人数小于 nums[i]。
 * 4. 遍历排序后的数组,统计满足条件的学生人数,并根据这些人数计算出满足所有学生开心的分组方法数。
 */
import java.util.Arrays;

public class Solution {
    public long countHappyGroups(int[] nums) {
        int n = nums.length;
        Arrays.sort(nums);
        return Arrays.stream(nums)
                     .boxed()
                     .collect(Collectors.groupingBy(i -> i))
                     .entrySet()
                     .stream()
                     .mapToInt(entry -> {
                         int count = entry.getValue().size();
                         return (count == 0 || nums[count - 1] < count) && (count == n || nums[count] > count) ? 1 : 0;
                     })
                     .sum();
    }
}

解释

方法:

题解首先对数组 nums 进行排序。排序后,可以利用数组的顺序属性来判断哪些选中的学生人数可以使全部学生保持开心。\n\n对于每个学生 i ,要满足的条件是:\n1. 如果这位学生被选中,那么至少要有 nums[i] + 1 个学生被选中(因为要严格大于 nums[i])。\n2. 如果这位学生没有被选中,被选中的学生数必须严格小于 nums[i]。\n\n由于数组已经排序,nums[i] 是递增的,因此可以通过遍历排序后的数组,检查每个可能的学生人数 k 是否满足所有学生的开心条件:\n- nums[0] > 0 意味着没有学生被选中时,所有学生都会开心。\n- 对于 k = i (从 1 开始),如果 nums[i-1] < i < nums[i],则选择 i 个学生会使得所有学生保持开心。\n\n最终,算法统计并返回所有可能让学生开心的组合数量。

时间复杂度:

O(n log n)

空间复杂度:

O(1)

代码细节讲解

🦆
排序数组之后,为什么可以通过遍历排序后的数组来检查每个可能的学生人数 k 是否满足所有学生的开心条件?
排序数组后将学生的要求以非减序列方式组织起来,这样可以更方便地检查每个可能的学生人数k。由于数组是非减的,如果一个数k满足某个位置i的要求,那么它一定满足所有位置小于i的要求。同理,如果k不满足位置i的要求,那么它也不会满足所有位置大于i的要求。这种属性允许我们只需线性扫描一次排序后的数组,就能确定哪些学生人数k能使所有学生满意,大大提高了效率。
🦆
在题解中提到当`nums[0] > 0`时,会增加一种全选的方法,但是否应该是`nums[0] == 0`时增加全选的方法?
题解中的表述存在误导。实际上,如果`nums[0] == 0`,表示至少需要1个学生被选中以满足第一个学生的开心条件。因此,在`nums[0] == 0`的情况下我们应该增加全选的方法。如果`nums[0] > 0`,意味着即使没有学生被选中,第一个学生的要求也已经被满足,因此只需考虑全不选的方法。
🦆
题解中提到`如果 nums[i - 1] < i < nums[i]`时,增加一种方法。为什么这个条件能保证选择 i 个学生会使得所有学生保持开心?
条件`nums[i - 1] < i < nums[i]`意味着选中的学生数量i正好位于nums[i-1](前一个学生需要的最少数量)和nums[i](当前学生需要的最少数量)之间。这保证了前i个学生的需求都被满足(因为i大于他们的需求),同时不会超过第i+1个学生的需求(因为i小于该学生的需求)。这种精确匹配使所有学生都保持开心。
🦆
如果数组中有重复的元素,这种方法是否还能正确地计算出所有可能的开心的分组方法数?
即使数组中有重复元素,这种方法仍然有效。重复元素意味着有多个学生具有相同的开心条件。在这种情况下,只要选择的学生数满足这些重复条件中的任何一个,就自然也满足了其他所有相同的条件。因此,重复元素不会影响这种基于排序和条件检查的方法来确定所有可能使学生开心的分组数。

相关问题