1. 题目解析与算法设计
睡眠质量评估是一个经典的分类统计问题。题目要求我们根据每天的睡眠时长,将其划分为四个质量等级,然后找出最差的那个等级(按照差>良>优>极好的优先级)及其出现次数。
1.1 输入输出分析
输入格式非常明确:
- 第一行:天数n(2≤n≤1,000,000)
- 第二行:n个整数,表示每天的睡眠时间(0≤a_i≤24)
输出要求:
- 最差质量等级对应的字符串(Bad/Good/Great)
- 该等级出现的天数
1.2 质量等级判定规则
根据题目描述,质量等级划分如下:
- 极好:≥12小时
- 优:9-11小时
- 良:5-8小时
- 差:0-4小时
需要注意的是,题目保证至少存在两种不同的质量等级,这避免了所有天数都是同一等级的特殊情况。
1.3 算法选择
由于数据规模可能达到1,000,000,我们需要一个O(n)时间复杂度的算法。直接遍历统计各等级天数是最优选择:
- 初始化四个计数器(差、良、优、极好)
- 遍历每天的睡眠时间,根据区间增加对应计数器
- 按优先级(差>良>优)检查哪个最差等级有数据
- 输出结果
2. 代码实现详解
2.1 数据结构设计
使用数组c[4]来存储四个等级的计数:
- c[0]:差(0-4小时)
- c[1]:良(5-8小时)
- c[2]:优(9-11小时)
- c[3]:极好(≥12小时)
注意:数组索引从0开始更符合C++惯例,但原题代码使用了1-based索引。这里我们保持与题目一致。
2.2 核心逻辑实现
cpp复制#include<bits/stdc++.h>
using namespace std;
long long n, a[1000006], c[5]; // c[1]-差, c[2]-良, c[3]-优, c[4]-极好
int main() {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
if(a[i] <= 4) {
c[1]++;
} else if(a[i] <= 8) {
c[2]++;
} else if(a[i] <= 11) {
c[3]++;
} else {
c[4]++;
}
}
if(c[1] >= 1) {
cout << "Bad" << endl << c[1] << endl;
} else if(c[2] >= 1) {
cout << "Good" << endl << c[2] << endl;
} else if(c[3] >= 1) {
cout << "Great" << endl << c[3] << endl;
} else {
cout << c[4] << endl; // 根据题意,这种情况不会出现
}
return 0;
}
2.3 关键点解析
-
输入处理:使用
cin读取大规模数据在C++中可能较慢,但题目给出的数据范围(n≤1e6)在大多数OJ系统中是可接受的。如果n更大(如1e7以上),建议使用更快的输入方式。 -
条件判断顺序:检查质量等级的优先级非常重要。必须先检查差(c[1]),然后是良(c[2]),最后是优(c[3])。这种顺序确保了输出的是"最差"的等级。
-
边界情况:题目保证至少存在两种质量等级,所以最后的else分支实际上不会执行,但为了代码完整性仍然保留。
3. 算法优化与注意事项
3.1 时间复杂度分析
- 数据读取:O(n)
- 分类统计:O(n)
- 结果判断:O(1)
总体时间复杂度为O(n),对于n≤1e6完全足够。
3.2 空间优化
当前实现使用了两个数组:
- a[1000006]存储所有输入数据
- c[5]存储计数
实际上可以优化掉a数组,因为我们在读取每个数据后立即进行分类,不需要保存所有数据:
cpp复制long long n, x, c[5];
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> x;
if(x <= 4) c[1]++;
else if(x <= 8) c[2]++;
else if(x <= 11) c[3]++;
else c[4]++;
}
这种优化将空间复杂度从O(n)降低到O(1)。
3.3 输入输出效率
对于C++,在处理大规模数据时(n≈1e6),cin/cout可能比scanf/printf慢。可以通过以下方式加速:
cpp复制ios::sync_with_stdio(false);
cin.tie(0);
或者在极端情况下使用更快的输入函数。
3.4 常见错误
-
条件判断顺序错误:如果先检查优再检查差,会导致输出结果不符合题目要求的优先级。
-
数组越界:确保数组大小足够(如a[1000006]而不是a[1000000],因为n可以达到1e6)。
-
未初始化计数器:局部变量不会自动初始化为0,必须手动初始化c数组。
4. 测试用例设计
为了验证代码的正确性,应该设计各种边界情况的测试用例:
4.1 基本测试用例
plaintext复制输入:
5
1 9 4 5 8
输出:
Bad
2
解释:差(1,4)出现2次,良(5,8)出现2次,优(9)出现1次。最差是差。
4.2 边界值测试
plaintext复制输入:
3
4 5 12
输出:
Bad
1
解释:差(4)出现1次,虽然良(5)和极好(12)也存在,但差是最差的。
4.3 大规模数据测试
生成n=1e6的数据,包含各种质量等级的随机分布,验证程序性能和正确性。
4.4 特殊分布测试
plaintext复制输入:
4
11 11 12 12
输出:
Great
2
解释:只有优(11)和极好(12),最差的是优。
5. 算法扩展思考
5.1 多维度质量评估
实际问题中,睡眠质量可能不仅取决于时长,还受入睡时间、深度睡眠比例等因素影响。可以扩展为多维度的评估模型:
cpp复制struct SleepRecord {
int duration; // 睡眠时长
int deepSleep; // 深度睡眠时长
int bedtime; // 入睡时间(小时)
};
int evaluateQuality(const SleepRecord& r) {
int score = 0;
if(r.duration >= 12) score += 3;
else if(r.duration >= 9) score += 2;
else if(r.duration >= 5) score += 1;
if(r.deepSleep >= r.duration/3) score += 1;
if(r.bedtime <= 23) score += 1;
return score; // 总分0-5
}
5.2 动态阈值调整
固定的时间阈值可能不适合所有人。可以考虑基于个人历史数据动态调整阈值:
cpp复制// 计算个人平均睡眠时间
double avg = accumulate(data.begin(), data.end(), 0.0) / data.size();
// 动态调整阈值
double good_threshold = avg * 0.8;
double excellent_threshold = avg * 1.2;
5.3 可视化输出
对于实际应用,可以增加可视化输出,如生成睡眠质量趋势图:
python复制import matplotlib.pyplot as plt
days = range(1, n+1)
qualities = ['Bad' if x<=4 else 'Good' if x<=8 else 'Great' if x<=11 else 'Excellent' for x in data]
plt.plot(days, data)
plt.xlabel('Day')
plt.ylabel('Sleep hours')
plt.title('Sleep Quality Trend')
plt.show()
6. 实际应用建议
在实际开发睡眠监测应用时,除了基础统计外,还可以考虑:
- 连续睡眠质量分析:识别连续多天睡眠不足的模式
- 提醒功能:当检测到连续多天睡眠质量差时发出提醒
- 个性化建议:根据睡眠模式给出改善建议
- 数据导出:支持将睡眠数据导出为CSV或其他格式
例如,检测连续睡眠不足:
cpp复制int consecutiveBadDays = 0;
int maxConsecutive = 0;
for(int i = 1; i <= n; i++) {
if(a[i] <= 4) {
consecutiveBadDays++;
maxConsecutive = max(maxConsecutive, consecutiveBadDays);
} else {
consecutiveBadDays = 0;
}
}
if(maxConsecutive >= 3) {
cout << "Warning: " << maxConsecutive << " consecutive bad sleep days!" << endl;
}
这个简单的睡眠质量统计问题展示了如何将现实需求转化为算法实现。关键在于:
- 正确理解题目要求和优先级规则
- 选择合适的数据结构和算法
- 处理边界条件和特殊输入
- 考虑性能和可扩展性
对于初学者来说,这类题目很好地训练了基础编程能力和逻辑思维。在实际应用中,可以在此基础上扩展更复杂的睡眠分析功能。