[SMOJ1786]考试排名

97 篇文章 0 订阅
9 篇文章 0 订阅

题目描述

ACM 编程考试使用的实时提交系统,具有即时获得成绩排名的特点。它的功能是怎么实现的呢?

我们编好题目的程序,提交之后,要么“AC”,要么错误,不管怎样错法,总是给你记上一笔,表明你曾经有过这么一次错误提交,因而当你一旦提交该题“AC”后,就要与你算一算帐了,总共该题错误提交了几回。虽然你在题数上,大步地跃上了一个台阶,但是在耗时上要摊上你共花去的时间。特别是,曾经有过的错误提交,每次都要摊上一定的单位罚时时间分。这样一来,你在做出的题数上,可能领先别人很多,但是,在做出同样题数的人群中,你可能会在耗时上处于排名的劣势。

例如:某次考试一共8题 (A,B,C,D,E,F,G,H),每个人做的题都在对应的题号下有个数量标记,负数表示该学生在该题上有过的错误提交次数,但到现在还没有 AC,正数表示 AC 所耗的时间,如果正数 a 跟上一对括号,里面有个整数 b,那就表示该学生提交该题 AC了,耗去了时间 a ,同时,曾经错误提交了 b 次,因此对于下述输入数据:

若每次错误提交的罚时时间分为 20 分,则其排名从高到低应该是这样的:

  • Josephus 5 376
  • John 4 284
  • Alice 4 352
  • Smith 3 167
  • Bob 2 325
  • Bush 0 0

输入格式 1786.in

输入数据的第一行三个整数 n ,m k ,分别表示考试题数 n 以及单位罚时时间分 m 和参加考试的学生人数 k

接下来 k 行,每行数据描述一个考试学生的信息,依次为用户名(不多于 10 个字符的字串)以及对所有 n 道题的答题现状,其描述格式采用问题描述中的数量标记的格式(中间可能有多个空格,空格个数不是定数),见下面的样例,提交次数总是小于 100,AC 所耗时间总是大于 0 小于 1000。

输出格式 1786.out

将这些学生的考试现状,输出一个实时排名。实时排名显然先按 AC 题数的多少排,多的在前,再按时间分的多少排,少的在前,如果凑巧前两者都相等,则按名字的字典序排,小的在前。每个学生占一行,输出名字(10 个字符宽,左对齐),做出的题数(2 个字符宽,右对齐)和时间分(4 个字符宽,右对齐)。名字、题数和时间分相互之间有一个空格。(详见下发样例)

输入样例 1786.in

输出样例 1786.out


这题算是比较简单的一题,直接按题意模拟就可以了,但是要注意一些细节。

一个是字符串处理,其实也可以用格式化读入,但是对于括号还是要单独判断处理。算出每个人的 AC 题数,所用时间(含罚时)后按以下优先级排一遍序:

  • 若两者 AC 题数不相同,则 AC 多的在前;
  • 若两者 AC 题数相同,用时不同,则用时少的在前;
  • 若两者 AC 题数和用时都相同,则姓名字典序小的在前。

最后再注意一下输出的格式,就可以了。

参考代码:

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace std;

const int maxk = 1e4;

struct ACMer {
    char name[20];
    int ac;
    int time;
    ACMer () {
        ac = 0; time = 0;
    }
    bool operator < (const ACMer x) const {
        if (ac != x.ac) return ac > x.ac;
        else if (time != x.time) return time < x.time;
        else return strcmp(name, x.name) < 0;
    }
} a[maxk];

int n, m, k;

int tonum(char *s, int l, int r) { //将字符数组 s 中的 [l, r] 转为整数
    int ret = 0;
    for (int i = l; i <= r; i++) ret = ret * 10 + s[i] - '0';
    return ret;
}

void solve(int x, char *s) { //处理选手 x 的某题信息
    int l = strlen(s);
    bool passed = l > 1 || s[0] != '0'; //是否 AC 该题
    int leftBracket = -1, rightBracket = -1; //找左右括号(如果有)
    for (int i = 0; i < l; i++)
        if (s[i] == '(') leftBracket = i;
        else if (s[i] == ')') rightBracket = i;
        else if (s[i] == '-') passed = false;
    if (passed) {
        a[x].ac++;
        if (leftBracket == -1) a[x].time += tonum(s, 0, l - 1); //如果一次就 A 了
        else a[x].time += tonum(s, 0, leftBracket - 1) + tonum(s, leftBracket + 1, rightBracket - 1) * m; //算上罚时
    }
}

int main(void) {
    freopen("1786.in", "r", stdin);
    freopen("1786.out", "w", stdout);
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 0; i < k; i++) {
        scanf("%s", a[i].name);
        for (int j = 0; j < n; j++) {
            char str[20];
            scanf("%s", str);
            solve(i, str);
        }
    }
    sort(a, a + k);
    for (int i = 0; i < k; i++) printf("%-10s%2d%4d\n", a[i].name, a[i].ac, a[i].time);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值