题解 P7426 【[THUPC2017] 体育成绩统计】

· · 题解

Update

Content

太长了,请直接跳转回题面查看。

数据范围:n\leqslant 10^40\leqslant a,b\leqslant 590\leqslant c\leqslant 100m\leqslant 10^50\leqslant l\leqslant 1000\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 函数,都能够起到自定义排序的作用。

这道题目对于阳光长跑的合法条件实在是多,但我们只能够一个一个去对了,建议按照以下次序:

合法记录的数量就这样搞定了。那么从这里开始就很简单了!首先,这一部分的分数只需要一波 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;
}