Medium
题目描述
给你一个下标从 0 开始、长度为 n 的整数数组 nums,其中 n 是班级中学生的总数。班主任希望选择一组学生,使得所有学生都保持快乐。
如果满足下述两个条件之一,第 i 个学生将会保持快乐:
- 这名学生被选中,且被选中的学生人数 严格大于 nums[i] 。
- 这名学生没有被选中,且被选中的学生人数 严格小于 nums[i] 。
返回能够满足让所有学生都快乐的方案数。
示例 1:
输入:nums = [1,1]
输出:2
解释:
有 2 种可能的方法:
- 班主任没有选择任何学生。
- 班主任选择所有学生形成一组。
如果班主任仅选择一个学生来组成小组,那么两个学生都无法保持快乐。因此,仅有 2 种可能的方法。
示例 2:
输入:nums = [6,0,3,3,6,7,2,7]
输出:3
解释:
有 3 种可能的方法:
- 班主任选择下标为 1 的学生形成一组。
- 班主任选择下标为 1、2、3、6 的学生形成一组。
- 班主任选择所有学生形成一组。
提示:
1 <= nums.length <= 10^50 <= nums[i] < nums.length
解题思路
这道题的核心思想是理解学生快乐的条件,并找出所有可能的选择方案数。
首先分析题意:
- 如果学生被选中,那么选中的总人数必须严格大于该学生的期望值
- 如果学生没被选中,那么选中的总人数必须严格小于该学生的期望值
关键观察:对于任何有效的选择方案,如果我们按照 nums 值排序,那么必然存在一个分界点,使得分界点左侧的学生都被选中,右侧的学生都不被选中。这是因为如果一个期望值较小的学生不被选中,那么期望值更大的学生也不应该被选中。
解题步骤:
- 对数组进行排序
- 枚举所有可能的选择人数 k(从 0 到 n)
- 对于每个 k,检查是否满足所有学生的快乐条件:
- 前 k 个学生(被选中):选择人数 k 必须严格大于他们的期望值
- 后 n-k 个学生(未被选中):选择人数 k 必须严格小于他们的期望值
这样我们就能统计出所有有效的方案数。
代码实现
class Solution {
public:
int countWays(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
int ways = 0;
for (int k = 0; k <= n; k++) {
bool valid = true;
// 检查被选中的学生是否快乐
for (int i = 0; i < k; i++) {
if (k <= nums[i]) {
valid = false;
break;
}
}
// 检查未被选中的学生是否快乐
if (valid) {
for (int i = k; i < n; i++) {
if (k >= nums[i]) {
valid = false;
break;
}
}
}
if (valid) {
ways++;
}
}
return ways;
}
};
class Solution:
def countWays(self, nums: List[int]) -> int:
nums.sort()
n = len(nums)
ways = 0
for k in range(n + 1):
valid = True
# 检查被选中的学生是否快乐
for i in range(k):
if k <= nums[i]:
valid = False
break
# 检查未被选中的学生是否快乐
if valid:
for i in range(k, n):
if k >= nums[i]:
valid = False
break
if valid:
ways += 1
return ways
public class Solution {
public int CountWays(IList<int> nums) {
var sortedNums = nums.OrderBy(x => x).ToArray();
int n = sortedNums.Length;
int ways = 0;
for (int k = 0; k <= n; k++) {
bool valid = true;
// 检查被选中的学生是否快乐
for (int i = 0; i < k; i++) {
if (k <= sortedNums[i]) {
valid = false;
break;
}
}
// 检查未被选中的学生是否快乐
if (valid) {
for (int i = k; i < n; i++) {
if (k >= sortedNums[i]) {
valid = false;
break;
}
}
}
if (valid) {
ways++;
}
}
return ways;
}
}
var countWays = function(nums) {
nums.sort((a, b) => a - b);
const n = nums.length;
let ways = 0;
for (let k = 0; k <= n; k++) {
let valid = true;
// 检查被选中的学生是否快乐
for (let i = 0; i < k; i++) {
if (k <= nums[i]) {
valid = false;
break;
}
}
// 检查未被选中的学生是否快乐
if (valid) {
for (let i = k; i < n; i++) {
if (k >= nums[i]) {
valid = false;
break;
}
}
}
if (valid) {
ways++;
}
}
return ways;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 外层循环 O(n),内层最坏情况遍历 O(n),排序 O(n log n) |
| 空间复杂度 | O(1) | 除了排序可能需要的额外空间外,只使用常量额外空间 |