LuoguP7426 [THUPC2017] 体育成绩统计 题解
Update
- \(\texttt{2021.3.11}\) 修复了一个笔误。
Content
太长了,请直接跳转回题面查看。
数据范围:\(n\leqslant 10^4\),\(0\leqslant a,b\leqslant 59\),\(0\leqslant c\leqslant 100\),\(m\leqslant 10^5\),\(0\leqslant l\leqslant 100\),\(0\leqslant s\leqslant 10^6\)。
变量含义同原题面。
Solution
很烦人的一道大模拟。
既然题目说成绩分五部分,那我们的程序也分五部分进行:
Part 1 体育课专项成绩
这个直接输入完就往总分数里面加,不需要多讲。
Part 2 长跑成绩
读入的时候运用了一个小 trick:我们知道 scanf
是可以按照格式读入的,所以直接用 "%d'%d\""
的格式读入即可【注意!在字符串里面想要打入 "
需要在其前面加一个反斜杠(\
)】。
然后,直接拿分钟数和秒数去对比显然有点麻烦,我们不妨直接将其转化为秒数。我们都知道 \(1\) 分钟有 \(60\) 秒,所以将读入的时间直接转化成秒数就是 \(a\times 60+b\)。再去比对题目中的表格。把题目中的表格中的时间转化成秒数,得到一个新的表格(建议去题解界面查看):
长跑得分 | \(20\) | \(18\) | \(16\) | \(14\) | \(12\) | \(10\) | \(8\) | \(6\) | \(4\) | \(2\) |
---|---|---|---|---|---|---|---|---|---|---|
男生/秒 | \(750\) | \(780\) | \(810\) | \(840\) | \(870\) | \(910\) | \(950\) | \(990\) | \(1030\) | \(1080\) |
女生/秒 | \(400\) | \(417\) | \(434\) | \(451\) | \(470\) | \(485\) | \(500\) | \(515\) | \(530\) | \(540\) |
然后直接一波 if-else 搞定。
Part 3 阳光长跑
本题的重头戏,因为要考虑的情况实在是太多了。
首先要将对应的长跑数据转入到对应的人中。这里如果直接开数组的话,显然,\(10^4\times 1.5\times 10^5=1.5\times 10^ 9\) 显然空间爆炸,所以考虑开个 vector
,读入的时候将所有的数据储存到一个结构体变量当中,再直接转入到对应的人中去。
这里又有一个 trick:我们通过 STL 中的 map
,将所有的人的学号映射到其在输入数据中的编号,这样处理起来就很简单,不需要再去暴力循环找了。
所有的长跑原始数据都转移到对应的人上面去后,我们注意到,在每个人底下的长跑原始数据并不一定是有序的,然而题目当中 开始时间需与上条合法记录的结束时间间隔 \(6\) 小时以上(包含 \(6\) 小时) 这个条件要求我们一定要对这些数据进行一定程度的排序。
既然这样那就对这些数据排序吧,这里你可以直接在结构体下面定义重载运算符,也可以直接弄一个 compare
函数,都能够起到自定义排序的作用。
这道题目对于阳光长跑的合法条件实在是多,但我们只能够一个一个去对了,建议按照以下次序:
- 如果男生跑步路程 \(<3000\) 米,女生跑步路程 \(<1500\) 米,那么这条记录就不合法。
- 否则,如果跑步速度 \(<2\operatorname{m/s}\),或者 \(>5\operatorname{m/s}\),或者平均步幅 \(>1.5\operatorname{m/s}\),或者总休息时间 \(>270\) 秒(同样也是用的处理长跑成绩时的 trick),那么这条记录不合法。
- 否则,如果目前记录是这位同学的第 \(1\) 条长跑记录,那么这条记录就已经是合法的了,计入计数器。
- 如果这条记录不是第 \(1\) 条记录,那么继续往下判断。提取出上条记录的结束时间的年、月、日、时、分、秒和目前记录的开始时间的年、月、日、时、分、秒。
- 如果月份不一样,那么判断是否超过了 \(1\) 个月。超过了一个月的话这条记录就是合法的。
- 否则这两条记录就是相邻月份的,然后我们再看上次合法记录的结束时间是不是在上一个月的最后一天,且该条记录的开始时间是不是在本月的第一天。如果不是,那么这条记录就是合法的。
- 否则,这两天其实只相隔一天,我们再看看它们的间隔时间是否大于 \(6\) 小时,也就是 \(21600\) 秒即可。月份不一样的就算处理完了。
- 接下来,如果这两个日期不在同一天,我们再看相隔天数,如果超过 \(1\) 天,那么这个记录就是合法的。
- 否则,那么这两个日期相隔天数正好一天,我们只需要看这两个记录的间隔时间是否大于 \(6\) 小时即可。天份不一样的情况也处理完了。
- 最后,这两个日期正好是在同一天,直接拿两个时间相减看时间是否大于 \(6\) 小时即可。
合法记录的数量就这样搞定了。那么从这里开始就很简单了!首先,这一部分的分数只需要一波 if-else 就能够搞定。
Part 4 体质测试成绩
只需要看给出的字符是否是 P
,是的话分数 \(+10\),否则不变。
Part 5 大一专项计划
这里又分为两部分。
Part 5.1 出勤
由题目可知,次数就等于班级训练营的参加次数和在 Part 3 里面处理完的阳光长跑合法记录次数的和。得到出勤次数之后又是一波 if-else 搞定分数。
Part 5.2 期末检测
这个直接输入完就往总分数里面加,不需要多讲。
最后的输出等第也是直接一波 if-else 搞定。
那么这道题目也就算做完了。最后友情提示,做这道题目的时候一定要沉下心来一步一步去做!不然稍有个一不留神 \(100\) 分就全没有了。
下面给出具体代码实现,配有详细注释,仅供参考,请勿抄袭。
Code
struct sunnyrun {
int y, m, d, hs, ms, ss, ht, mt, st, rest, step;
double l;
bool operator < (const sunnyrun& tmp) const {
//按照记录开始时间排序
if(y != tmp.y) return y < tmp.y;
else if(m != tmp.m) return m < tmp.m;
else if(d != tmp.d) return d < tmp.d;
else if(hs != tmp.hs) return hs < tmp.hs;
else if(ms != tmp.ms) return ms < tmp.ms;
return ss < tmp.ss;
}
};
struct student {
int mf, s, a, b, pf, f, c, score, num2, num;
ll p;
vector<sunnyrun> q;
/*
mf:1 表示男生,0 表示女生
pf:1 表示通过,0 表示没有通过
*/
}a[10007];
const int mm[17] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
double l;
map<ll, int> vis;
inline int calcms(int a, int b) {return a * 60 + b;}
// 分秒形式的时间转换成秒
inline int calchms(int a, int b, int c) {return a * 3600 + b * 60 + c;}
// 时分秒形式的时间转换成秒
inline int s1(int x) {return a[x].s;} //直接计入分数
inline int s2(int x) {
if(a[x].mf) {
if(calcms(a[x].a, a[x].b) <= 750) return 20;
else if(calcms(a[x].a, a[x].b) > 750 && calcms(a[x].a, a[x].b) <= 780) return 18;
else if(calcms(a[x].a, a[x].b) > 780 && calcms(a[x].a, a[x].b) <= 810) return 16;
else if(calcms(a[x].a, a[x].b) > 810 && calcms(a[x].a, a[x].b) <= 840) return 14;
else if(calcms(a[x].a, a[x].b) > 840 && calcms(a[x].a, a[x].b) <= 870) return 12;
else if(calcms(a[x].a, a[x].b) > 870 && calcms(a[x].a, a[x].b) <= 910) return 10;
else if(calcms(a[x].a, a[x].b) > 910 && calcms(a[x].a, a[x].b) <= 950) return 8;
else if(calcms(a[x].a, a[x].b) > 950 && calcms(a[x].a, a[x].b) <= 990) return 6;
else if(calcms(a[x].a, a[x].b) > 990 && calcms(a[x].a, a[x].b) <= 1030) return 4;
else if(calcms(a[x].a, a[x].b) > 1030 && calcms(a[x].a, a[x].b) <= 1080) return 2;
else return 0; // 如果上面的都不符合一定要记得 return 0!不然可能会 RE
} else {
if(calcms(a[x].a, a[x].b) <= 400) return 20;
else if(calcms(a[x].a, a[x].b) > 400 && calcms(a[x].a, a[x].b) <= 417) return 18;
else if(calcms(a[x].a, a[x].b) > 417 && calcms(a[x].a, a[x].b) <= 434) return 16;
else if(calcms(a[x].a, a[x].b) > 434 && calcms(a[x].a, a[x].b) <= 451) return 14;
else if(calcms(a[x].a, a[x].b) > 451 && calcms(a[x].a, a[x].b) <= 470) return 12;
else if(calcms(a[x].a, a[x].b) > 470 && calcms(a[x].a, a[x].b) <= 485) return 10;
else if(calcms(a[x].a, a[x].b) > 485 && calcms(a[x].a, a[x].b) <= 500) return 8;
else if(calcms(a[x].a, a[x].b) > 500 && calcms(a[x].a, a[x].b) <= 515) return 6;
else if(calcms(a[x].a, a[x].b) > 515 && calcms(a[x].a, a[x].b) <= 530) return 4;
else if(calcms(a[x].a, a[x].b) > 530 && calcms(a[x].a, a[x].b) <= 540) return 2;
else return 0; // 同上
}
}
inline int s3(int x) {
int cnt = 0, flg1 = 0; sunnyrun lst;
sort(a[x].q.begin(), a[x].q.end()); //按照记录开始时间排序
int sze = a[x].q.size();
F(i, 0, sze - 1) {
sunnyrun now = a[x].q[i];
if(now.l < (a[x].mf ? 3.0 : 1.5)) continue; //路程不达标,不合法
int ts = calchms(now.hs, now.ms, now.ss), tt = calchms(now.ht, now.mt, now.st);
double v = now.l * 1000.0 / (tt - ts), pe = now.l * 1000.0 / now.step;
if(v < 2 || v > 5 || pe > 1.5 || now.rest > 270) continue; //速度、步幅、总休息时间中任意一个不达标,不合法
if(!flg1) {flg1 = 1, lst = a[x].q[i], cnt++; continue;} //以上条件都达标且是第一个记录,合法
ts = calchms(lst.ht, lst.mt, lst.st), tt = calchms(now.hs, now.ms, now.ss); //开始时间和结束时间
if(now.m != lst.m) { //不是一个月
if(now.m - 1 != lst.m) {lst = now, cnt++; continue;} //间隔超过一个月,合法
else {
if(mm[lst.m] != lst.d || now.d != 1) {lst = now, cnt++; continue;} //上条合法记录的结束时间是上个月的最后一天、该条记录的开始时间是这个月的第一天当中有一条不符合,合法
else if(86400 + tt - ts < 21600) continue; //间隔只有一天,并且间隔时间小于 6 小时,不合法
}
} else if(now.d != lst.d) { //不是同一天
if(now.d - 1 != lst.d) {lst = now, cnt++; continue;} //间隔天数超过 1 天,合法
else if(86400 + tt - ts < 21600) continue; //间隔只有一天,并且间隔时间小于 6 小时,不合法
} else if(tt - ts < 21600) continue; //间隔时间小于 6 小时,不合法
lst = now, cnt++; //合法
}
a[x].num2 = cnt; //计入次数
if(a[x].num2 >= 21) return 10;
else if(a[x].num2 >= 19 && a[x].num2 <= 20) return 9;
else if(a[x].num2 >= 17 && a[x].num2 <= 18) return 8;
else if(a[x].num2 >= 14 && a[x].num2 <= 16) return 7;
else if(a[x].num2 >= 11 && a[x].num2 <= 13) return 6;
else if(a[x].num2 >= 7 && a[x].num2 <= 10) return 4;
else if(a[x].num2 >= 3 && a[x].num2 <= 6) return 2;
else return 0;
}
inline int s4(int x) {return a[x].pf ? 10 : 0;} //直接计入分数
inline int s5(int x) {
a[x].num = a[x].c + a[x].num2;
if(a[x].num >= 18) return 5 + a[x].f; //出勤次数和期末检测成绩综合判断
else if(a[x].num >= 15 && a[x].num <= 17) return 4 + a[x].f;
else if(a[x].num >= 12 && a[x].num <= 14) return 3 + a[x].f;
else if(a[x].num >= 9 && a[x].num <= 11) return 2 + a[x].f;
else if(a[x].num >= 6 && a[x].num <= 8) return 1 + a[x].f;
else return a[x].f;
}
int main() {
int n = Rint;
F(i, 1, n) {
a[i].p = Rll; char ch[2]; scanf("%s", ch);
a[i].mf = (ch[0] == 'M'); //性别是否是男
a[i].s = Rint;
scanf("%d'%d\"", &a[i].a, &a[i].b); //强制按照 a'b" 的格式读入 a,b
scanf("%s", ch); a[i].pf = (ch[0] == 'P');
a[i].f = Rint, a[i].c = Rint; vis[a[i].p] = i; //建立映射关系
}
int m = Rint;
F(i, 1, m) {
sunnyrun ins; //待插入元素
int date = Rint; ins.y = date / 10000, ins.m = date % 10000 / 100, ins.d = date % 100;
ll px = Rll; int id = vis[px]; //映射出该学生在输入数据中的编号
scanf("%d:%d:%d %d:%d:%d", &ins.hs, &ins.ms, &ins.ss, &ins.ht, &ins.mt, &ins.st); //强制按照 hh:mm:ss hh:mm:ss 的格式读入该条记录的开始时间和结束时间的时分秒
scanf("%lf", &ins.l); //路程
int ax, bx; scanf("%d'%d\"", &ax, &bx); ins.rest = calcms(ax, bx), ins.step = Rint; //强制按照 a'b" 的格式读入 a,b
a[id].q.push_back(ins); //插入对应结构体元素下的 vector 中
}
F(i, 1, n) {
a[i].score = s1(i) + s2(i) + s3(i) + s4(i) + s5(i); //五个部分的分数合计
printf("%lld %d ", a[i].p, a[i].score);
if(a[i].score >= 95 && a[i].score <= 100) puts("A"); //判断等第
else if(a[i].score >= 90 && a[i].score < 95) puts("A-");
else if(a[i].score >= 85 && a[i].score < 90) puts("B+");
else if(a[i].score >= 80 && a[i].score < 85) puts("B");
else if(a[i].score >= 77 && a[i].score < 80) puts("B-");
else if(a[i].score >= 73 && a[i].score < 77) puts("C+");
else if(a[i].score >= 70 && a[i].score < 73) puts("C");
else if(a[i].score >= 67 && a[i].score < 70) puts("C-");
else if(a[i].score >= 63 && a[i].score < 67) puts("D+");
else if(a[i].score >= 60 && a[i].score < 63) puts("D");
else puts("F");
}
return 0;
}
LuoguP7426 [THUPC2017] 体育成绩统计 题解的更多相关文章
- 体育成绩统计——20180801模拟赛T3
体育成绩统计 / Score 题目描述 正所谓“无体育,不清华”.为了更好地督促同学们进行体育锻炼,更加科学地对同学们进行评价,五道口体校的老师们在体育成绩的考核上可谓是煞费苦心.然而每到学期期末时, ...
- 体育成绩统计/ Score
偏水向,请部分学术控谅解 题目过长,不再描述. 很显然就是一道大模拟对吧,我在这里贡献一下我打此题的思路与过程. 或许有些奇淫巧技可以供一些没有过掉的神犇借鉴一下. 2020.11.26 中午: 昨天 ...
- 2016福州大学软件工程第二次团队作业——预则立&&他山之石成绩统计
第二次团队作业--预则立&&他山之石成绩统计结果如下: T:团队成绩 P:个人贡献比 T+P:折算个人成绩,计算公式为T+T/15*团队人数*P 学号 组别 Team P T+P 03 ...
- sdut 3-5 学生成绩统计
3-5 学生成绩统计 Time Limit: 1000MS Memory limit: 65536K 题目描写叙述 通过本题目练习能够掌握对象数组的使用方法,主要是对象数组中数据的输入输出操作. 设计 ...
- (注意输入格式)bistuoj(旧)1237 成绩统计
成绩统计 Time Limit(Common/Java):1000MS/3000MS Memory Limit:65536KByteTotal Submit:88 ...
- 成绩统计程序(Java)
我的程序: package day20181018;/** * 成绩统计系统 * @author Administrator */import java.util.Scanner;//提供计算机直接扫 ...
- YTU 2798: 复仇者联盟之数组成绩统计
2798: 复仇者联盟之数组成绩统计 时间限制: 1 Sec 内存限制: 128 MB 提交: 136 解决: 96 题目描述 定义一个5行3列的二维数组,各行分别代表一名学生的高数.英语.C++ ...
- YTU 2769: 结构体--成绩统计
2769: 结构体--成绩统计 时间限制: 1 Sec 内存限制: 128 MB 提交: 1021 解决: 530 题目描述 建立一个简单的学生信息表,包括:姓名.性别.年龄及一门课程的成绩,统计 ...
- 【Java例题】7.5 文件题2-学生成绩统计
5.学生成绩统计.已有一个学生成绩文件,含有多位学生的各三门课的成绩:读取这个文件中的每位学生的三门课成绩,然后计算均分:最后对这些均分按照大于或小于75分的界限,分别写到另两个文件中. packag ...
随机推荐
- 使用国内的镜像源搭建 kubernetes(k8s)集群
1. 概述 老话说的好:努力学习,提高自己,让自己知道的比别人多,了解的别人多. 言归正传,之前我们聊了 Docker,随着业务的不断扩大,Docker 容器不断增多,物理机也不断增多,此时我们会发现 ...
- 新玩法-使用AllArgsConstructor+filal代替autowired
和下面的代码一样: Springboot官方建议使用final来修饰成员变量,然后通过构造方法来进行注入原因:final修饰的成员变量是不能够被修改的,反射那就没办法了 还有一种写法: @Requir ...
- 【程序员翻身计划】Java高性能编程第一章-Java多线程概述
目标 重点: 线程安全的概念 线程通信的方式与应用 reactor线程模型 线程数量的优化 jdk常用命令 Netty框架的作用 难点 java运行的原理 同步关键字的原理 AQS的抽象 JUC的源码 ...
- 快来使用Portainer让测试环境搭建飞起来吧
Portainer是Docker的图形化管理工具,提供状态显示面板.应用模板快速部署.容器镜像网络数据卷的基本操作(包括上传下载镜像,创建容器等操作).事件日志显示.容器控制台操作.Swarm集群和服 ...
- CF1373G
考虑中间格子不能有相同的点,其实是没用的. 其唯一用处是用来规定最后的是无法重叠的. 我们可以证明最后位置的无重叠和中间不重叠是充要的. 那显然可以我们对每个点往后连边: 形式的话的说: 对 \((x ...
- DirectX12 3D 游戏开发与实战第七章内容(下)
利用Direct3D绘制几何体(续) 学习目标 学会一种无须每帧都要刷新命令队列的渲染流程,由此来优化程序的性能 了解另外两种跟签名参数类型:根描述符和根常量 探索如何在程序中生成和绘制常见的几何体, ...
- R数据科学-1
R数据科学(R for Data Science) Part 1:探索 by: PJX for 查漏补缺 exercise: https://jrnold.github.io/r4ds-exercis ...
- Go 命令类型和未命名类型
Go 命令类型和未命名类型 例子 package main import "fmt" // 使用type声明的是命令类型 // type new_type old_type typ ...
- MYSQL5.8----M3
333333333333333333333333333 mysql> DESC user; +----------+---------------------+------+-----+---- ...
- 在WEB网页上模拟人的操作(批量操作)
思路:selenium IDE网页测试工具+firefox浏览器=>录制网页操作脚本->导出为Perl/python/Ruby/C/R等语言 参考: (1)selenium IDE网页测试 ...