1141. PAT Ranking of Institutions (25)
After each PAT, the PAT Center will announce the ranking of institutions based on their students' performances. Now you are asked to generate the ranklist.
Input Specification:
Each input file contains one test case. For each case, the first line gives a positive integer N (<=105), which is the number of testees. Then N lines follow, each gives the information of a testee in the following format:
ID Score School
where "ID" is a string of 6 characters with the first one representing the test level: "B" stands for the basic level, "A" the advanced level and "T" the top level; "Score" is an integer in [0, 100]; and "School" is the institution code which is a string of no more than 6 English letters (case insensitive). Note: it is guaranteed that "ID" is unique for each testee.
Output Specification:
For each case, first print in a line the total number of institutions. Then output the ranklist of institutions in nondecreasing order of their ranks in the following format:
Rank School TWS Ns
where "Rank" is the rank (start from 1) of the institution; "School" is the institution code (all in lower case); "TWS" is the total weighted score which is defined to be the integer part of "ScoreB/1.5 + ScoreA + ScoreT*1.5", where "ScoreX" is the total score of the testees belong to this institution on level X; and "Ns" is the total number of testees who belong to this institution.
The institutions are ranked according to their TWS. If there is a tie, the institutions are supposed to have the same rank, and they shall be printed in ascending order of Ns. If there is still a tie, they shall be printed in alphabetical order of their codes.
Sample Input:10 A57908 85 Au B57908 54 LanX A37487 60 au T28374 67 CMU T32486 24 hypu A66734 92 cmu B76378 71 AU A47780 45 lanx A72809 100 pku A03274 45 hypuSample Output:
5 1 cmu 192 2 1 au 192 3 3 pku 100 1 4 hypu 81 2 4 lanx 81 2
和乙级的 1085. PAT单位排行 (25) 是一样的。
本来是一道水题,可是又变成优化题,难怪通过率挺低的。
题意就是PAT每次考试完,姥姥在微博发的每个学校的成绩排行,现在让你模拟这个过程。总体考查多标尺排序和STL。
大概有这么三种做法:
方案一:定义10000大小的数组node,用来保存每个学校的信息。然后用map保存每个学校对应的id,id是int类型的,就是node的下标。然后用一个全局变量index记录有几个学校,最后排序sort(node, node + index, cmp)就行了。详情看代码:
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
#include <vector>
#define MAX 100000 + 10
using namespace std;
map<string, int> mpInt;
map<int, string> mpStr;
typedef struct Node {
Node() { school = score = cnt = 0; }
int school;
int score;
int cnt;
} Node;
Node node[MAX];
int index = 0;
int cmp( Node a, Node b ) {
if( a.score != b.score ) return a.score > b.score;
if( a.cnt != b.cnt ) return a.cnt < b.cnt;
return mpStr[a.school] < mpStr[b.school];
}
int main() {
int n;
scanf( "%d", &n );
string id, school;
int score;
for( int i = 0; i < n; i++ ) {
cin >> id >> score >> school;
// 学校名转成小写
for( int j = 0; j < school.size(); j++ ) {
if( school[j] >= 'A' && school[j] <= 'Z' ) school[j] = school[j] + 32;
}
// 计算转换后的分数
switch( id[0] ) {
case 'B': score = score * 1.0 / 1.5; break;
case 'A': break;
case 'T': score = score * 1.0 * 1.5; break;
}
if( !mpInt.count( school ) ) {
mpInt[school] = index;
mpStr[index] = school;
node[index].school = index;
node[index].cnt++;
node[index].score = node[index].score + (int)score;
index++;
}
else {
int cur = mpInt[school];
node[cur].cnt++;
node[cur].score = node[cur].score + (int)score;
}
}
sort( node, node + index, cmp );
printf( "%d\n", index );
int pre = -1;
int cur;
for( int i = 0; i < index; i++ ) {
if( pre == -1 ) cur = i + 1;
else if( pre != node[i].score ) cur = i + 1;
pre = node[i].score;
printf( "%d %s %d %d\n", cur, mpStr[node[i].school].c_str(), node[i].score, node[i].cnt );
}
return 0;
}
然后就超时了。。因为我们用map的时候,定义的类型是map<string, int>。string比char*要慢,小数据规模没啥效果,数据量一大就超时了。所以我们优化的时候就用map<char*, int>。但如果map的key用char*就很烦了,一般也不这么做。具体key为char*的坑如何解决看这个
https://blog.csdn.net/zark721/article/details/63685919
另外还有一个地方需要注意,map<char*, int> 中的key我定义的是学校的字符串,读输入的时候不能把char school[7]放在main函数里,因为要读n次数据,每次都会把school的值刷新一遍,这样的话map去找每次找的都是同一个值,我们应该定义一个school[10000][7],或者定义char* school,每次malloc分配内存(我也不知道该怎么解释,凑合着理解吧),具体可以看我程序。根据这个思路实现第二个做法。
方案二:使用map<char*, int>
#include <cstdio>
#include <map>
#include <cstring>
#include <algorithm>
#include <vector>
#define MAX 100000 + 10
using namespace std;
typedef struct Node {
Node() { score = cnt = 0; }
char school[7];
double score;
int cnt;
} Node;
Node node[MAX];
struct ptrCmp {
bool operator()( const char * s1, const char * s2 ) const {
return strcmp( s1, s2 ) < 0;
}
};
map<char*, int, ptrCmp> mp;
int Index = 0;
int cmp( Node a, Node b ) {
if( (int)a.score != (int)b.score ) return (int)a.score > (int)b.score;
if( a.cnt != b.cnt ) return a.cnt < b.cnt;
return strcmp( a.school, b.school ) < 0 ? 1 : 0;
}
//char school[MAX][7];
int main() {
int n;
scanf( "%d", &n );
char id[7];
char* school;
double score;
for( int i = 0; i < n; i++ ) {
school = (char*)malloc( sizeof( char ) * 7 );
scanf( "%s %lf %s", id, &score, school );
//printf( "%s %s\n", id, school );
// 学校名转成小写
for( int j = 0; j < strlen( school ); j++ ) {
if( school[j] >= 'A' && school[j] <= 'Z' ) school[j] = school[j] + 32;
}
// 计算转换后的分数
switch( id[0] ) {
case 'B': score = score / 1.5; break;
case 'A': break;
case 'T': score = score * 1.5; break;
}
if( !mp.count( school ) ) {
mp[school] = Index;
strcpy( node[Index].school, school );
node[Index].cnt++;
node[Index].score = node[Index].score + score;
Index++;
}
else {
int cur = mp[school];
node[cur].cnt++;
node[cur].score = node[cur].score + score;
}
}
sort( node, node + Index, cmp );
printf( "%d\n", Index );
int pre = -1;
int cur;
for( int i = 0; i < Index; i++ ) {
if( pre == -1 ) cur = i + 1;
else if( pre != (int)node[i].score ) cur = i + 1;
pre = (int)node[i].score;
printf( "%d %s %d %d\n", cur, node[i].school, (int)node[i].score, node[i].cnt );
}
return 0;
}
然后就AC了,哈哈哈哈,速度超级快。
方案三:见网上大部分AC的是用两个map<string, int>,一个统计人数,另一个统计学校的加权总分。然后遍历map,把每个学校的信息push_back到vector中,然后再排序,不太明白这么做为什么会快(相对于方案一),但是这么做能AC。具体实现看下面代码:
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <vector>
#define MAX 100000 + 10
using namespace std;
typedef struct Node {
Node() { score = cnt = 0; }
string school;
int score;
int cnt;
} Node;
vector<Node> vec;
map<string, int> mpCnt;
map<string, double> mpScore;
int cmp( Node a, Node b ) {
if( a.score != b.score ) return a.score > b.score;
if( a.cnt != b.cnt ) return a.cnt < b.cnt;
return a.school < b.school;
}
int main() {
int n;
scanf( "%d", &n );
string id, school;
double score;
for( int i = 0; i < n; i++ ) {
cin >> id >> score >> school;
// 学校名转成小写
for( int j = 0; j < school.size(); j++ ) {
if( school[j] >= 'A' && school[j] <= 'Z' ) school[j] = school[j] + 32;
}
// 计算转换后的分数
switch( id[0] ) {
case 'B': score = score / 1.5; break;
case 'A': break;
case 'T': score = score * 1.5; break;
}
if( mpCnt.count( school ) ) {
mpCnt[school]++;
mpScore[school] = mpScore[school] + score;
}
else {
mpCnt[school] = 1;
mpScore[school] = score;
}
}
Node t;
for( map<string, int>::iterator it = mpCnt.begin(); it != mpCnt.end(); it++ ) {
t.school = it->first;
t.cnt = it->second;
t.score = (int)mpScore[it->first];
vec.push_back( t );
}
sort( vec.begin(), vec.end(), cmp );
int len = vec.size();
printf( "%d\n", len );
int pre = -1;
int cur;
for( int i = 0; i < len; i++ ) {
if( pre == -1 ) cur = i + 1;
else if( pre != vec[i].score ) cur = i + 1;
pre = vec[i].score;
printf( "%d %s %d %d\n", cur, vec[i].school.c_str(), vec[i].score, vec[i].cnt );
}
return 0;
}
能AC,但险些超时,运气不好说不定最后一个测试点就GG了。。优点是内存占用小。
方案四(我自己临时想的):对于方案一,如果我们不知道map<char*, int>怎么用,可能就放弃用map的想法了。由于题目中说学校是一个不超过7个长度的字符串,如果我们自己写一个hash函数,然后用char*读输入,说不定也能AC,但具体怎么实现这个哈希函数我没想好。或者用unordered_map说不定也能AC,但需要支持C++11,我没试PAT的编译器是否支持。
另外此题还有一个坑:题目中说最后学校的加权和是整数,我们程序计算的时候先用double,最后输出的时候强转成int。当计算N个学生成绩的时候,因为要*1.5或/1.5,就产生小数了,如果你每次都把double转成int,最后精度就错误了,导致最后一个测试点答案错误,这个就很坑了。。
希望对路过的同学有帮助