题目描述
根据做题数、时间以及姓名字典序获取实时成绩排名。其中,每个人做的题都在对应的题号下有个数量标记,负数表示该学生在该题上有过的错误提交次数但到现在还没有AC,正数表示AC所耗的时间,如果正数a跟上了一对括号,里面有个正数b,则表示该学生AC了这道题,耗去了时间a,同时曾经错误提交了b次。未AC的题目,耗时不算入时间内,AC的题目,其时间为a+b*罚时。
Input
输入数据包含多行,第一行是共有的题数n(1≤n≤12)以及单位罚时m(10≤m≤20),之后的每行数据描述一个学生的信息,首先是学生的用户名(不多于10个字符的字串)其次是所有n道题的得分现状,其描述采用问题描述中的数量标记的格式。
Output
根据这些学生的得分现状,输出一个实时排名。实时排名显然先按AC题数的多少排,多的在前,再按时间分的多少排,少的在前,如果凑巧前两者都相等,则按名字的字典序排,小的在前。每个学生占一行,输出名字(10个字符宽),做出的题数(2个字符宽,右对齐)和时间分(4个字符宽,右对齐)。名字、题数和时间分相互之间有一个空格。数据保证可按要求的输出格式进行输出。
解题思路
- 建立结构体,用于记录每个学生的做题信息,用数组来储存不同学生,并用sort函数进行排序;
- 利用string来读取学生做题信息,因为string默认以空格和换行为分界,且不会读入换行,而Input数据恰好以空格进行分隔,因此只需以学生姓名为大分割线,依次读入获取每道题的信息,每读入一次处理一次,即可获取每一名学生的做题信息;
- 调整输出格式(仔细读题!)。
实现代码
#include<iostream>
#include<string>
#include<algorithm>
#include<iomanip>
using namespace std;
struct Rank
{//结构体内包括每个人的姓名、AC数、时间,并重载<以使用sort排序
string name;
int number;
int time;
bool operator<(const Rank &p) const
{
if(number!=p.number)
return number>p.number;
if(time!=p.time)
return time<p.time;
return name<p.name;
}
};
Rank P[1000];
int main()
{
int n,m,i=0;
cin>>n>>m;
string s;
while(cin>>s)
{
P[i].name=s;
P[i].number=0;
P[i].time=0;
for(int j=0;j<n;++j)
{//读取每一个人的n道做题信息
cin>>s;
//未AC,相关信息不计入
if(s[0]=='0'||s[0]=='-')
continue;
if(s.find("(")==string::npos)
{//AC且无括号,代表没有罚时
int a=0;
//计算对应花费时间,计入该学生的time总值
for(int k=0;k<s.size();++k)
a=a*10+s[k]-'0';
P[i].time+=a;
}
else
{//AC且有括号,有罚时
int k=0,a=0,b=0;
while(s[k]!='(')
{//计算花费时间
a=a*10+s[k]-'0';
k++;
}
k++;
while(s[k]!=')')
{//计算错误提交次数
b=b*10+s[k]-'0';
k++;
}
//花费时间+罚时一同计入该学生time总值
P[i].time+=a;
P[i].time+=b*m;
}
//有AC,该学生AC数+1
P[i].number++;
}
//记录下一个学生的信息
i++;
}
//对i个学生排序,并按照指定格式输出
sort(P,P+i);
for(int j=0;j<i;++j)
cout<<setw(10)<<left<<P[j].name<<" "<<setw(2)<<right<<P[j].number
<<" "<<setw(4)<<right<<P[j].time<<endl;
return 0;
}
总结
本题实际上是一个结构体多关键字排序的问题,只要在结构体内重载<,确定相应的排序方式,直接用sort完成排序即可。
本题难点在于如何将Input数据准确读入,在这里我使用string,借用其特性,依次读入,逐步处理,能够实现信息的准确读入,解决这一问题后,剩下的就交给sort完成即可。