题意:
例如某次考试一共八道题(A,B,C,D,E,F,G,H),每个人做的题都在对应的题号下有个数量标记:-3表示该学生在该题上有3次错误提交次数但还没AC,1表示AC的耗时,40(3) 表示该学生AC这道题耗时40,错误提交了3次。一共n道题,单位罚时为m。根据这些学生的得分,输出一个实时排名。采用多关键字排序,第一按AC题数,多的在前;第二按按时间分,少的在前;第三按名字的字典序,小的在前。输出时每个学生占一行,输出名字(10个字符宽),做出的题数(2个字符宽,右对齐)和时间分(4个字符宽,右对齐)。
样例输入:
样例输出:
思路:
首先从屏幕上读入每一个同学的数据,整理成所需的名字、AC题数、时间分。定义一个结构体info来存每个学生的信息,读入名字后因为知道题数所以循环读入数据,每次读入都进行处理,换算为AC题数和时间分,然后累加。
所有学生的数据都读完后,整理后的信息也存在了结构体数组中,根据多关键字,利用sort()函数进行排序。最后按照规定格式输出。
根据一个学生的数据算AC题数和时间分是解题关键。负数和0没有意义,本解法利用re_change()函数,把正数和带括号的数一起处理。找到括号前的数在字符串中的起始和终点位置[0,end),那么括号中的数是[end+1,strlen(s)-1),调用change()函数把字符串转化为整数。
总结:
因为读入数据时,数据与数据之间没有关联,所以采用边读入边换算的方法较好,还可以节省空间。
在cout输出时,如果限制字宽将默认右对齐,setiosflags(ios::left)可以设置左对齐。如果左右对齐交替输出,需要用resetiosflags(ios::left)解除左对齐,右对齐同理。
代码:
#include<iostream>
#include<string>
#include <algorithm>
#include<iomanip>
using namespace std;
struct info
{
string name;
int ti;
int time;
};
bool com(info a, info b)
{
if (a.ti != b.ti) return a.ti > b.ti;
else if (a.time != b.time) return a.time < b.time;
else return a.name[0] < b.name[0];
}
int change(string a, int begin, int end)//把字符串从[begin,end)变为数字
{
int re = 0;
for (int i = begin; i < end; i++)
re = re * 10 + a[i] - 48;
return re;
}
int re_change(string s,int t)//把字符串变为数字
{
int len = s.size();
int begin = 0;//用时开始
int end = len;//用时结束、罚时开始
for (int k = 0; k < len; k++)
{
if (s[k] == 40)
{
end = k;
break;
}
}
int yt = change(s, begin, end);//用时
if (end != len)
{
int ft = change(s, end + 1, len - 1);//罚时
return yt + ft * t;
}
else
return yt;
}
int main()
{
int n, t;
cin >> n;
cin >> t;
int di = 0;
info everyone[1000];//存储整理后的信息
while (cin >> everyone[di].name)
{
int tis = 0;
int sum = 0;
for (int i = 0; i < n; i++)
{
string score;
cin >> score;
if (score[0] == '-' || score[0] == 48)
continue;
else
{
tis++;
sum = sum + re_change(score, t);
}
}
everyone[di].ti = tis;
everyone[di].time = sum;
di++;
}
sort(everyone, everyone + di, com);
for (int i = 0; i < di; i++)
cout << setiosflags(ios::left) << setw(10) << everyone[i].name <<" "
<< setiosflags(ios::right) << setw(2) << everyone[i].ti<<" "
<< setw(4) << everyone[i].time
<< resetiosflags(ios::right)<< endl;
}
另一种解法:
(感觉没错,跑不过去也是很迷惑)
思路:
本解法按行读入,然后再根据空格分割字符串。其余处理字符串、排序等同上。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include <algorithm>
#define N 1000
using namespace std;
struct info
{
char name[20];
int ti;
int time;
};
bool com(info a, info b)
{
if (a.ti != b.ti) return a.ti > b.ti;
else if (a.time != b.time) return a.time < b.time;
else return a.name[0] < b.name[0];
}
int change(char* a,int begin,int end)//把字符串从[begin,end)变为数字
{
int re=0;
for(int i=begin;i<end;i++)
re=re*10+a[i]-48;
return re;
}
int main()
{
int n,t;
scanf("%d", &n);
scanf("%d", &t);
getchar();
char*str=new char[100];
int i=0;
info everyone[N];//存储整理后的信息
while (scanf("%[^\n]", str))
{
char** slipt = new char*[n + 1];
for (int j = 0; j < n + 1; j++)
slipt[j] = new char[20];
for (int j = 0; j < n + 1 ; j++)//分割字符串
{
sscanf(str,"%s", slipt[j]);
char* pos2 = str + strlen(slipt[j]);//pos2是剩余的字符串
str=pos2;
char* p=str;
int len=0;
//剩余字符串头有空格,需要去掉这些空格
while (*p != '\0' && isspace(*p))
{
++p;
++len;
}
memmove(str, p, strlen(str) - len + 1);
}
strcpy(everyone[i].name,slipt[0]);
//计算时间
int tis=0;
int sum=0;
for(int j=1;j<=n;j++)
{
if(slipt[j][0]=='-'||slipt[j][0]==48)
continue;
else
{
tis++;
int len=strlen(slipt[j]);
int begin=0;//用时开始
int end=len;//用时结束、罚时开始
for(int k=0;k<len;k++)
{
if(slipt[j][k]==40)
{
end=k;
break;
}
}
int yt=change(slipt[j],begin,end);//用时
if(end!=len)
{
int ft=change(slipt[j],end+1,len-1);//罚时
sum=sum+yt+ft*t;
}
else
sum=sum+yt;
}
}
everyone[i].ti=tis;
everyone[i].time=sum;
str[0] = '\0';
getchar();
i++;
for (int j = 0; j < n + 1; j++)
delete[]slipt[j];
}
sort(everyone, everyone + i, com);
for(int ik=0;ik<i;ik++)
printf("%-10s %2d %4d\n",everyone[ik].name,everyone[ik].ti,everyone[ik].time);
}