0%

实时排名

实时排名

问题描述

程序设计思维作业和实验使用的实时评测系统,具有及时获得成绩排名的特点,那它的功能是怎么实现的呢?
我们千辛万苦怼完了不忍直视的程序并提交以后,评测系统要么返回AC,要么是返回各种其他的错误,不论是怎样的错法,它总会给你记上一笔,表明你曾经在这儿被坑过,而当你历经千辛终将它AC之后,它便会和你算笔总账,表明这题共错误提交了几次。
在岁月的长河中,你通过的题数虽然越来越多,但通过每题时你所共花去的时间(从最开始算起,直至通过题目时的这段时间)都会被记录下来,作为你曾经奋斗的痕迹。特别的,对于你通过的题目,你曾经的关于这题的每次错误提交都会被算上一定的单位时间罚时,这样一来,你在做出的题数上,可能领先别人很多,但是在做出同样题数的人中,你可能会因为罚时过高而处于排名上的劣势。
例如某次考试一共八道题(A, B, C, D, E, F, G, H),每个人做的题都在对应的题号下有个数量标记,负数表示该学生在该题上有过的错误提交次数但到现在还没有AC,正数表示AC所耗的时间,如果正数a跟上了一对括号,里面有个正数b,则表示该学生AC了这道题,耗去了时间a,同时曾经错误提交了b次。例子可见下方的样例输入与输出部分。

Input

输入数据包含多行,第一行是共有的题数 n (1 ≤ n ≤ 12)以及单位罚时 m (10 ≤ m ≤ 20),之后的每行数据描述一个学生的信息,首先是学生的用户名(不多于10个字符的字串)其次是所有 n 道题的得分现状,其描述采用问题描述中的数量标记的格式。
负数表示尝试但没通过的次数,正数表示得分,正数+括号表示得分+尝试没通过的次数。

Output

根据这些学生的得分现状,输出一个实时排名。实时排名显然先按AC题数的多少排,多的在前,再按时间分的多少排,少的在前,如果凑巧前两者都相等,则按名字的字典序排,小的在前。每个学生占一行,输出名字(10个字符宽),做出的题数(2个字符宽,右对齐)和时间分(4个字符宽,右对齐)。名字、题数和时间分相互之间有一个空格。数据保证可按要求的输出格式进行输出。

Sample

input:
8 20
GuGuDong 96 -3 40(3) 0 0 1 -8 0
hrz 107 67 -3 0 0 82 0 0
TT 120(3) 30 10(1) -3 0 47 21(2) -2
OMRailgun 0 -99 -8 0 -666 -10086 0 -9999996
yjq -2 37(2) 13 -1 0 113(2) 79(1) -1
Zjm 0 0 57(5) 0 0 99(3) -7 0

output:
TT 5 348
yjq 4 342
GuGuDong 3 197
hrz 3 256
Zjm 2 316
OMRailgun 0 0

解题思路

要实现不难,但是,格式很烦,没注意就会WA

由于不知道要录入几个人,所以要while()一直录入,判断eof()
对于第二行起的每一行输入,首先读入名字,然后对n道题依次读入,判断读入第0位:'-''0'不操作,否则丢入realScore()函数获取真实分数,并且通过题数pass+1,再将这些分数累加得到最终的得分point(详见代码)。

输出按照输出格式输出:
10个字符宽: setw(10)
左对齐: left
右对齐: right
名字、题数和时间分相互之间有一个空格,由于已经进行了对其操作,我写的时候还真漏了,看着不管改了好几次都是WA或者CE真的好烦……
排序:冒泡排序

有些坑……

  1. cin之后如果使用getline(cin, string)会把cin之后的换行符读入,所以getline第一次读入的是换行符。
    解决:cin.ignore(0x7fffffff, '\n');把换行符消除。

  2. VJ上面G++编译一直WAC++编译一直Complication Error,而且提示居然是"What do you want to do?"???(本地测试没问题啊)
    Google的时候发现:
    在C++中读取一行的getline函数是不读入换行符的,而gcc / g++ 中getline函数是读入换行符的
    但是我本地没法弄个g++来测试,索性就不用getline()了(本来是每次读入一行,然后通过sstream对读入的这一行以空格分解为多个string进行操作)。

  3. 按名字字典排序
    虽然说名字字长10以内,但是再对名字逐字符对比ASCII码排序,有点麻烦啊……就想想能不能简单的通过<>对整个string进行对比,Google了一下,发现了compare()

    int string::compare (const string& str) const
    Returns:
    0 : if both strings are equal.
    A value < 0 : if *this is shorter than str or,
    first character that does not match is smaller than str.
    A value > 0 : if *this is longer than str or,
    first character that does not match is greater

    也就是说string s1如果字典序在string s2的前面,则s1.compare(s2) < 0

  4. 每位学生分数信息用一个结构体struct score储存,每个score我丢到vector里面储存,因为使用比较方便,不需要注意数组的大小,每次只需要push_back()就行。但是……VJ上一直过不了啊!!!(编译器是多老啊😓)
    所以后来改成用数组储存,容量开大一点,因为也不知道会录入多少。

源代码

#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <algorithm>
using namespace std;

const int maxnum = 10001;

/* 学生成绩结构体 */
struct score {
string name;//名字
int n;//本次成绩个数,不过用不到
int p;//pass//多少个通过
int sc;//scores//总得分
score() { n = 0; p = 0; sc = 0; }
};

/* 求单项真实分数(原正得分+罚时分),结果返回int分数 */
int realScore(string s, int m) {
bool tried = false;//bool 尝试过 = false,即都对,没被罚时
//扫描左右括号
for (int i = 0; i != s.size(); i++) {
if (s.at(i) == '(') {
s.at(i) = ' ';//左括号变成空格,方便下一步处理
tried = true;//扫到的话,有被罚时
}
if (s.at(i) == ')') {
s.at(i) = ' ';//右括号变成空格,方便下一步处理
tried = true;//再写一次 以防万一
}

}
for (int i = 0; i != s.size(); i++) {//以防万一string里面不是0~9
if (!(s.at(i) >= 48 && s.at(i) <= 57))
s.at(i) = ' ';
}
if (tried) {//如果有罚时
istringstream iss(s);//处理string 使得可以转成int
int a, b;
iss >> a >> b;//用于处理string 空格分隔 存进两个int里面
return (a + b * m);//a = 原得分 b = 尝试次数 m = 单位罚时
}
else {//没有罚时
istringstream iss(s);
int a;
iss >> a;//string转成int
return a;//直接返回得分
}
}

/* 用于对比名字 Alphabetically */
bool com(string a, string b) {
return a.compare(b) > 0;//如果名字a字母表顺序在名字b后 则返回true
}

int main() {
int n, m;
cin >> n >> m;

score arrayList[maxnum];// list of names & scores
cin.ignore(0x7fffffff, '\n');//忽略cin后的换行符 防止下次读入时出错

int countNum = 0;//计数 录入多少个学生的信息
string name;//姓名
while (!(cin >> name).eof()) {//没读到EOF就一直执行
countNum++;//已录入 计数+1

string temp;//临时string 储存得分
int pass = 0;//多少题通过 初始为0
int point = 0;//得分从0开始算
//int tried = 0;//pass but failed how many times
for (int i = 0; i < n; i++) {//题意 n道题
cin >> temp;
if (temp.at(0) != '-' && temp.at(0) != '0') {//判断
//若非负分或0分 就可以记分
pass++;//通过题数+1
point += realScore(temp, m);//丢到函数里计算真实得分 并累加到point
}
}

score s;
s.name = name;
s.n = n;
s.p = pass;
s.sc = point;

arrayList[countNum - 1] = s;//每个学生的信息存到数组arrayList里
}

//after input, order list
//bubble sort
for (int i = 0; i < countNum - 1; i++) {
for (int j = 0; j < countNum - i - 1; j++) {
if (arrayList[j].p < arrayList[j + 1].p)//若通过题数较小
swap(arrayList[j], arrayList[j + 1]);//换位
if (arrayList[j].p == arrayList[j + 1].p) {//若通过题数相等 继续判断
if (arrayList[j].sc > arrayList[j + 1].sc)//若总分较大
swap(arrayList[j], arrayList[j + 1]);//换位
if (arrayList[j].sc == arrayList[j + 1].sc)//若总分相等
if (com(arrayList[j].name, arrayList[j + 1].name))//若名字靠后
swap(arrayList[j], arrayList[j + 1]);//换位
}
}
}

for (int i = 0; i != countNum; i++) {//按格式输出
cout << left << setw(10) << arrayList[i].name << " " <<
right << setw(2) << arrayList[i].p << " " <<
right << setw(4) << arrayList[i].sc << endl;
}

return 0;
}