洛谷 - P7426 [THUPC 2017] 体育成绩统计
题目链接
考察知识点
- 算法 - 模拟
思路分析
一道丧心病狂的大模拟
依据题意模拟即可
时间复杂度
\(O(n+m)\)
C++ 代码
#include <map> // 用于建立学号与学生索引的映射
#include <iostream> // 用于输入输出操作
#include <vector> // 用于存储每个学生的阳光长跑记录
using namespace std;
const int N = 1e4 + 10; // 学生最大数量(10010),防止数组越界
map<int, int> h; // 映射表:key为学号,value为学生在数组中的索引(便于快速查找学生)
/*
体育课总评成绩构成(满分100分):
- 体育课专项成绩:满分50分
- 长跑测试成绩:满分20分
- 「阳光长跑」成绩:满分10分
- 体质测试成绩:满分10分(通过为10分,未通过为0分)
- 「大一专项计划」成绩:满分10分
- 出勤次数(阳光长跑+班级训练营):占5分
- 期末检测成绩:占5分(直接由输入提供)
*/
// 学生信息结构体,存储单个学生的所有基础数据
struct student {
int idx; // 学号(唯一标识)
int sex; // 性别:1表示男,2表示女
int PEScore; // 体育课专项成绩(满分50分)
int LongDisData; // 期末长跑测试时间(单位:秒)
int HealthScore; // 体质测试成绩(10分或0分)
int SpecialScore; // 「大一专项计划」的期末检测成绩(满分5分)
int ExerciseTimes; // 参加「班级训练营」的次数(用于计算出勤分)
int SunshineTimes; // 有效「阳光长跑」的次数(用于计算阳光长跑成绩和出勤分)
} stu[N]; // 存储所有学生信息的数组
// 阳光长跑记录结构体,存储单次跑步的详细数据
struct Record {
int date; // 跑步日期(格式:假设为年月日,如20231001表示2023年10月1日)
int Sa, Sb, Sc; // 开始时间:Sa=时,Sb=分,Sc=秒
int Ea, Eb, Ec; // 结束时间:Ea=时,Eb=分,Ec=秒
int a, b; // 暂停时间:a=分,b=秒(总暂停时间为a*60 + b秒)
int step; // 跑步步数
double dis; // 跑步距离(单位:公里,后续会转为米)
};
vector<Record> recVector[N]; // 存储每个学生的所有阳光长跑记录:recVector[i]为第i个学生的记录列表
// 每个月的天数(非闰年),用于将日期转换为当年的第几天
const int month[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int n, m; // n=学生数量,m=阳光长跑记录数量
// 将分钟和秒转换为总秒数(如a=1分,b=30秒 → 90秒)
int MinuteToSecond(int a, int b) {
return a * 60 + b;
}
// 将日期转换为当年的第几天(如3月1日 → 31(1月)+28(2月)+1=60天)
// 假设日期格式为:前四位为年份(未使用),后四位为月日(如xxxxMMDD)
int YearToDay(int date) {
// 提取月份和日期(从后四位中解析:MMDD)
int n5 = date / 1000 % 10; // 月份十位
int n6 = date / 100 % 10; // 月份个位
int n7 = date / 10 % 10; // 日期十位
int n8 = date / 1 % 10; // 日期个位
int Month = n5 * 10 + n6; // 月份(1-12)
int Day = n7 * 10 + n8; // 日期(1-31)
int all = 0;
// 累加当月之前所有月份的天数
for (int i = 1; i <= Month - 1; i++) {
all += month[i];
}
return all + Day; // 总天数=前几个月天数+当月日期
}
// 将时、分、秒转换为总秒数(如1时2分3秒 → 3600 + 120 + 3 = 3723秒)
int HourToSecond(int a, int b, int c) {
return a * 3600 + b * 60 + c;
}
/*
检查单次阳光长跑记录是否有效,有效则计入阳光长跑次数
参数说明:
- date, Sa, Sb, Sc, Ea, Eb, Ec, a, b, step, dis:单次跑步记录的原始数据
- sex:学生性别(1=男,2=女)
- last_end:引用传递,记录上一次有效跑步的结束时间(用于检查时间间隔)
返回值:true=有效,false=无效
*/
bool check(int date, int Sa, int Sb, int Sc, int Ea, int Eb, int Ec, int a, int b, int step, double dis, int sex, long long &last_end) {
// 1. 计算跑步开始和结束的总秒数(从当年1月1日0点0分0秒开始计算)
int day = YearToDay(date); // 日期转换为当年的第几天
// 开始时间总秒数 = (天数-1)*86400(一天秒数) + 当天时间秒数
long long StartSecond = (day - 1LL) * 86400 + HourToSecond(Sa, Sb, Sc);
// 结束时间总秒数(若跨天,结束时间可能大于86400,需加一天秒数)
long long EndSecond = (day - 1LL) * 86400 + HourToSecond(Ea, Eb, Ec);
if (EndSecond < StartSecond) { // 若结束时间在开始时间之前,说明跨天
EndSecond += 86400;
}
// 2. 检查与上一次有效跑步的时间间隔是否≥6小时(6*3600秒)
if (StartSecond - last_end < 6 * 3600) {
return false;
}
// 3. 检查步数是否为正数(无效步数)
if (step <= 0) {
return false;
}
// 4. 检查步距是否合理(步距=距离/步数,单位:米/步,需≤1.5米)
if (dis / step > 1.5) {
return false;
}
// 5. 检查暂停时间是否≤4分30秒(270秒)
int pause_seconds = MinuteToSecond(a, b); // 暂停时间总秒数
if (pause_seconds > 270) {
return false;
}
// 6. 检查跑步时长是否为正数(无效时长)
long long duration = EndSecond - StartSecond; // 跑步总时长(秒)
if (duration <= 0) {
return false;
}
// 7. 检查跑步速度是否合理(2米/秒 ≤ 速度 ≤5米/秒,速度=距离/时长)
double speed = dis / duration;
if (speed < 2 || speed > 5) {
return false;
}
// 8. 检查跑步距离是否达标(男生≥3000米,女生≥1500米)
if (sex == 1) { // 男生
if (dis < 3000) {
return false;
}
} else { // 女生
if (dis < 1500) {
return false;
}
}
// 所有条件均满足,更新上一次有效跑步的结束时间
last_end = EndSecond;
return true;
}
/*
根据性别和长跑测试时间(秒)计算长跑测试成绩(满分20分)
参数:
- sex:性别(1=男,2=女)
- sec:长跑测试时间(秒)
返回值:对应的分数(0-20分)
*/
int LongDisScoreJudge(int sex, int sec) {
if (sex == 1) { // 男生评分标准(对应不同时间区间的分数)
if (sec <= 12 * 60 + 30) return 20; // ≤12'30'' → 20分
else if (sec <= 13 * 60) return 18; // ≤13'00'' → 18分
else if (sec <= 13 * 60 + 30) return 16;// ≤13'30'' → 16分
else if (sec <= 14 * 60) return 14; // ≤14'00'' → 14分
else if (sec <= 14 * 60 + 30) return 12;// ≤14'30'' → 12分
else if (sec <= 15 * 60 + 10) return 10;// ≤15'10'' → 10分
else if (sec <= 15 * 60 + 50) return 8; // ≤15'50'' → 8分
else if (sec <= 16 * 60 + 30) return 6; // ≤16'30'' → 6分
else if (sec <= 17 * 60 + 10) return 4; // ≤17'10'' → 4分
else if (sec <= 18 * 60) return 2; // ≤18'00'' → 2分
else return 0; // >18'00'' → 0分
} else { // 女生评分标准(对应不同时间区间的分数)
if (sec <= 6 * 60 + 40) return 20; // ≤6'40'' → 20分
else if (sec <= 6 * 60 + 57) return 18; // ≤6'57'' → 18分
else if (sec <= 7 * 60 + 14) return 16; // ≤7'14'' → 16分
else if (sec <= 7 * 60 + 31) return 14; // ≤7'31'' → 14分
else if (sec <= 7 * 60 + 50) return 12; // ≤7'50'' → 12分
else if (sec <= 8 * 60 + 5) return 10; // ≤8'05'' → 10分
else if (sec <= 8 * 60 + 20) return 8; // ≤8'20'' → 8分
else if (sec <= 8 * 60 + 35) return 6; // ≤8'35'' → 6分
else if (sec <= 8 * 60 + 50) return 4; // ≤8'50'' → 4分
else if (sec <= 9 * 60) return 2; // ≤9'00'' → 2分
else return 0; // >9'00'' → 0分
}
}
/*
根据有效阳光长跑次数计算「阳光长跑」成绩(满分10分)
参数:t=有效阳光长跑次数
返回值:对应的分数(0-10分)
*/
int SunshineScoreJudge(int t) {
if (t >= 21) return 10; // ≥21次 → 10分
else if (t >= 19) return 9; // ≥19次 → 9分
else if (t >= 17) return 8; // ≥17次 → 8分
else if (t >= 14) return 7; // ≥14次 → 7分
else if (t >= 11) return 6; // ≥11次 → 6分
else if (t >= 7) return 4; // ≥7次 → 4分
else if (t >= 3) return 2; // ≥3次 → 2分
else return 0; // <3次 → 0分
}
/*
计算「大一专项计划」中的出勤分(满分5分)
出勤次数=阳光长跑有效次数 + 班级训练营参加次数
参数:
- a=阳光长跑有效次数
- b=班级训练营参加次数
返回值:对应的出勤分(0-5分)
*/
int ChuQinScoreJudge(int a, int b) {
int sum = a + b; // 总出勤次数
if (sum >= 18) return 5; // ≥18次 → 5分
else if (sum >= 15) return 4; // ≥15次 → 4分
else if (sum >= 12) return 3; // ≥12次 → 3分
else if (sum >= 9) return 2; // ≥9次 → 2分
else if (sum >= 6) return 1; // ≥6次 → 1分
else return 0; // <6次 → 0分
}
/*
根据总分数计算等级
参数:score=总分数(0-100分)
返回值:对应的等级字符串
*/
string GradeJudge(int score) {
if (score >= 95) return "A"; // ≥95 → A
else if (score >= 90) return "A-"; // ≥90 → A-
else if (score >= 85) return "B+"; // ≥85 → B+
else if (score >= 80) return "B"; // ≥80 → B
else if (score >= 77) return "B-"; // ≥77 → B-
else if (score >= 73) return "C+"; // ≥73 → C+
else if (score >= 70) return "C"; // ≥70 → C
else if (score >= 67) return "C-"; // ≥67 → C-
else if (score >= 63) return "D+"; // ≥63 → D+
else if (score >= 60) return "D"; // ≥60 → D
else return "F"; // <60 → F(不及格)
}
int main() {
scanf("%d", &n); // 输入学生数量
// 读取n个学生的基础信息
for (int i = 1; i <= n; i++) {
char sex, IsPass; // sex=性别('M'男/'F'女),IsPass=体质测试是否通过('P'通过/'N'未通过)
int a, b; // 用于存储长跑测试的分和秒(如a=12分,b=30秒)
// 读取学号、性别、体育课专项成绩
cin >> stu[i].idx >> sex >> stu[i].PEScore;
// 读取长跑测试时间(格式:a'b'',如12'30'')
scanf("%d\'%d\"", &a, &b);
// 读取体质测试是否通过、专项计划期末检测成绩、班级训练营次数
cin >> IsPass >> stu[i].SpecialScore >> stu[i].ExerciseTimes;
// 建立学号到学生索引的映射(方便后续查找)
h[stu[i].idx] = i;
// 转换性别为数字(1=男,2=女)
if (sex == 'M') {
stu[i].sex = 1;
} else {
stu[i].sex = 2;
}
// 体质测试成绩:通过为10分,未通过为0分
stu[i].HealthScore = (IsPass == 'P') ? 10 : 0;
// 将长跑测试时间转换为总秒数(存储在LongDisData中)
stu[i].LongDisData = MinuteToSecond(a, b);
// 初始化阳光长跑有效次数为0
stu[i].SunshineTimes = 0;
}
scanf("%d", &m); // 输入阳光长跑记录数量
// 读取m条阳光长跑记录,并按学生分组存储
for (int i = 1; i <= m; i++) {
int idx; // 学生学号
Record r; // 单次跑步记录
// 读取记录信息:日期、学号、开始时间、结束时间、距离(公里)、暂停时间、步数
// 时间格式:Sa:Sb:Sc(开始)、Ea:Eb:Ec(结束),暂停时间格式:a'b''
scanf("%d %d %d:%d:%d %d:%d:%d %lf %d\'%d\" %d",
&r.date, &idx,
&r.Sa, &r.Sb, &r.Sc, &r.Ea, &r.Eb, &r.Ec,
&r.dis, &r.a, &r.b, &r.step);
// 将记录添加到对应学生的记录列表
int student_index = h[idx]; // 获取学生在数组中的索引
recVector[student_index].push_back(r);
}
// 处理每个学生的阳光长跑记录,统计有效次数
for (int i = 1; i <= n; i++) {
// 若该学生有阳光长跑记录,则检查每条记录的有效性
if (!recVector[i].empty()) {
long long last_end = 0; // 记录上一次有效跑步的结束时间(初始为0)
// 遍历该学生的所有跑步记录
for (int j = 0; j < recVector[i].size(); j++) {
Record r = recVector[i][j];
// 将距离从公里转换为米(原单位为公里,乘以1000)
r.dis *= 1000;
// 检查记录是否有效,若有效则阳光长跑次数+1
if (check(r.date, r.Sa, r.Sb, r.Sc, r.Ea, r.Eb, r.Ec,
r.a, r.b, r.step, r.dis, stu[i].sex, last_end)) {
stu[i].SunshineTimes++;
}
}
}
}
// 计算每个学生的总分数和等级,并输出结果
for (int i = 1; i <= n; i++) {
// 计算各项成绩
int LongDisScore = LongDisScoreJudge(stu[i].sex, stu[i].LongDisData); // 长跑测试成绩
int SunshineScore = SunshineScoreJudge(stu[i].SunshineTimes); // 阳光长跑成绩
// 专项计划出勤分(阳光长跑次数+训练营次数)
int ChuQinScore = ChuQinScoreJudge(stu[i].SunshineTimes, stu[i].ExerciseTimes);
// 总分数 = 专项成绩 + 长跑成绩 + 阳光长跑成绩 + 体质测试成绩 + 专项计划期末分 + 出勤分
int score = stu[i].PEScore + LongDisScore + SunshineScore +
stu[i].HealthScore + stu[i].SpecialScore + ChuQinScore;
// 根据总分数获取等级
string grade = GradeJudge(score);
// 输出学号、总分数、等级
printf("%d %d ", stu[i].idx, score);
cout << grade << '\n';
}
return 0;
}

浙公网安备 33010602011771号