PAT甲级 排序题_C++题解

排序题

PAT (Advanced Level) Practice 排序题

目录

  • 《算法笔记》 6.9.6 sort()用法
  • 《算法笔记》 4.1 排序题步骤
  • 1012 The Best Rank (25)
  • 1083 List Grades (25)
  • 1137 Final Grading (25)
  • 1141 PAT Ranking of Institutions (25)
  • 1153 Decode Registration Card of PAT (25)

《算法笔记》 6.9.6 sort()

#include<algorithm>
using namespace std;
void sort (first, last, cmp);
// first 首元素地址;last 尾元素地址下一个地址;cmp 比较函数

《算法笔记》 4.1 排序题步骤

1. 相关结构体的定义

struct Student {
    char name[10];
    char id[10];
    int score;
    int rank;
}stu[10000];

2. cmp函数的编写

分数不同分数高在前,分数相同姓名字典序小在前

bool cmp(Student a, Student b){
    return a.score != b.score ? a.score > b.score : strcmp(a.name, b.name) < 0;
}

3. 排名的实现

分数不同排名不同,分数相同排名相同占用同一个排位

(1) 记录在结构体中
  1. 将数组第一个个体排名记为 1
  2. 遍历剩余个体,若当前个体分数 = 上一个个体分数,则当前个体排名 = 上一个体排名;
  3. 若当前个体分数 != 上一个体分数,则排名 = 数组下标 + 1
stu[0].rank = 1;
for (int i = 1; i < n; i++){
    if (stu[i].score == stu[i-1].score) stu[i].rank = stu[i-1].rank;
    else stu[i].rank = i + 1;
}
(2) 直接输出
int rank = 1;
for (int i = 0; i < n; i++)
    if (i > 0 && stu[i].score != stu[i-1].score)
        rank = i + 1;

1012 The Best Rank (25)

#include<unordered_map>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
struct Student{
	int id, best;
	int score[4], rank[4];
};
int sortby = -1;
bool cmp (Student a, Student b) { return a.score[sortby] > b.score[sortby];};
int main()
{
	int n, m, id;
	scanf("%d%d",&n,&m);
	vector<Student> stu(n+1);
	for (int i = 0; i < n; i++){
		scanf("%d%d%d%d",&stu[i].id,&stu[i].score[1],&stu[i].score[2],&stu[i].score[3]);
		stu[i].score[0] = (stu[i].score[1] + stu[i].score[2] + stu[i].score[3]) / 3;
	}
	for (sortby = 0; sortby < 4; sortby++){
		sort(stu.begin(),stu.end(),cmp);
		stu[0].rank[sortby] = 1;
		for (int i = 1; i < n; i++){
			if (stu[i].score[sortby] != stu[i-1].score[sortby]) stu[i].rank[sortby] = i+1;
			else stu[i].rank[sortby] = stu[i-1].rank[sortby];
		}
	}
	unordered_map<int, int> index;
	for (int i = 0; i < n; i++){
		index.insert(pair<int,int>(stu[i].id,i));
		stu[i].best = 0;
		int minr = stu[i].rank[0];
		for (int j = 1; j < 4; j++){
			if (stu[i].rank[j] < minr){
				stu[i].best = j;
				minr = stu[i].rank[j];
			}
		}
	}
	char symbol[] = "ACME";
	for (int i = 0; i < m; i++){
		scanf("%d",&id);
		if (index.find(id) == index.end()) printf("N/A\n");
		else{
			int best = stu[index[id]].best;
			printf("%d %c\n",stu[index[id]].rank[best], symbol[best]);
		}
	}
	return 0;
} 

简化代码(参考柳婼小姐姐的代码

  • 题目中平均分取整数,所以结构体中的分数信息均为 int 型,可用数组表示;同理依据不同分数进行的排名信息也可以用数组表示。
  • 对四种分数进行排序+排名需要四个不同的比较函数较为重复,且排序依据在数组中完全可以用数组下标移动来表示不同的比较依据。
    • 向 sort() 传入的 cmp() 参数固定不能增加,设置全局变量 sortby
    • 在 cmp() 中依据全局变量变化比较依据,在程序主体控制 sortby 以比较不同分数
  • 要比较四个不同的排名选择其中最好的,在结构体中设置变量 best 记录对应分数下标
    • 四种分数用一个简单的选择排序选出 best 进行赋值
    • 最后输出时依据记录的 best 到结构体自己的 rank 数组中输出最好的排名值 及 到字符数组 symbol 中输出对应的科目标识
      • 字符数组初始化问题 -> 笔记:3.5进制转换题 1027 Colors in Mars
  • 本题 id 可用整型表示,所以查找时相当于建立 int->int 的映射,可以直接开大数组做散列查找。
    • 由于之前将 id 设为 string 型,仍然使用 map,由于只用 map 做映射不做排序,可用 unordered_map
    • 注意添加映射关系要在处理完所有排名赋值后,否则sort会打乱排序,映射关系出错。
  • 要对 vector 使用 [] 访问进行赋值必须给出大小,不给大小只能用 push_back 添加元素
简化前代码如下
#include<unordered_map>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
struct Student{
	string id;
	int c, math, eng;
	double avg;
	int r_c, r_math, r_eng, r_avg;
	Student (){}
	Student (string _id, int _c, int _math, int _eng): id(_id), c(_c), math(_math), eng(_eng){
		avg = (c + math + eng) / 3.0;
	}
};
bool cmp_c (Student a, Student b){ return a.c > b.c;}
bool cmp_math (Student a, Student b){ return a.math > b.math;}
bool cmp_eng (Student a, Student b){ return a.eng > b.eng;}
bool cmp_avg (Student a, Student b){ return a.avg > b.avg;}
int main()
{
	int n, m, c, math, eng;
	string id;
	cin >> n >> m;
	vector<Student> stu;
	unordered_map<string, int> index;
	for (int i = 0; i < n; i++){
		cin >> id >> c >> math >> eng;
		stu.push_back(Student(id,c,math,eng));
	}
	sort(stu.begin(),stu.end(),cmp_c);
	stu[0].r_c = 1;
	for (int i = 1; i < n; i++){
		if (stu[i].c == stu[i-1].c) stu[i].r_c = stu[i-1].r_c;
		else stu[i].r_c = i+1;
	}
	sort(stu.begin(),stu.end(),cmp_math);
	stu[0].r_math = 1;
	for (int i = 1; i < n; i++){
		if (stu[i].math == stu[i-1].math) stu[i].r_math = stu[i-1].r_math;
		else stu[i].r_math = i+1;
	}
	sort(stu.begin(),stu.end(),cmp_eng);
	stu[0].r_eng = 1;
	for (int i = 1; i < n; i++){
		if (stu[i].eng == stu[i-1].eng) stu[i].r_eng = stu[i-1].r_eng;
		else stu[i].r_eng = i+1;
	}
	sort(stu.begin(),stu.end(),cmp_avg);
	stu[0].r_avg = 1;
	index.insert(pair<string,int>(stu[0].id,0));
	for (int i = 1; i < n; i++){
		if (stu[i].avg == stu[i-1].avg) stu[i].r_avg = stu[i-1].r_avg;
		else stu[i].r_avg = i+1;
		index.insert(pair<string,int>(stu[i].id,i));
	}
	for (int i = 0; i < m; i++){
		cin >> id;
		if (index.find(id) == index.end()) cout << "N/A\n";
		else{
			int min_r = min(min(min(stu[index[id]].r_c,stu[index[id]].r_math),stu[index[id]].r_eng),stu[index[id]].r_avg);
			cout << min_r << " ";
			if (min_r == stu[index[id]].r_avg) cout << "A\n";
			else if (min_r == stu[index[id]].r_c) cout << "C\n";
			else if (min_r == stu[index[id]].r_math) cout << "M\n";
			else cout << "E\n";
		}
	}
	return 0;
} 

1083 List Grades (25)

题目思路

  • 按成绩从大到小排序,排序后过滤输出。
  • 设置变量记录是否找到了值,没找到输出 NONE
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct Student{
	string name, id;
	int grade;
};
bool cmp(Student a, Student b){ return a.grade > b.grade; }
int main()
{
	int n, grade, min, max;
	scanf("%d", &n);
	string name, id;
	vector<Student> list;
	for (int i = 0; i < n; i++){
		cin >> name >> id >> grade;
		list.push_back({name, id, grade});
	}
	sort(list.begin(), list.end(), cmp);
	scanf("%d%d", &min, &max);
	bool empty = true;
	for (int i = 0; i < list.size(); i++){
		if (list[i].grade < min) break;
		if (list[i].grade > max) continue;
		if (empty) empty = false;
		cout << list[i].name << " " << list[i].id << endl;
	}
	if (empty) printf("NONE\n");
	return 0;
}

曾经清奇的脑回路Orz

  • 利用成绩唯一性,以成绩为 key 将姓名学号信息存入 map
  • 利用 map.lower_bound()map.upper_bound() 输出要求信息
#include<iostream>
#include<map>
using namespace std;
int main(){
    map<int,string,greater<int>> records;
    int n, grade, min, max;
    string name, ID;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        cin >> name >> ID >> grade;
        records.insert(pair<int,string>(grade,name + " " + ID));
    }
    scanf("%d%d", &min, &max);
    if (records.lower_bound(max) == records.upper_bound(min))
        cout << "NONE" << endl;
    else
        for (auto it = records.lower_bound(max); it != records.upper_bound(min); ++it)
            cout << it->second << endl;
    return 0;
}

1137 Final Grading (25)

题目思路

  • 将不同分数分别放到 map 中存储
  • 首要筛选编程分数低于 200 的,取记录编程分数的 map 进行遍历
  • 若编程分数低于 200,或没有登入期末考成绩,不可能合格,continue 跳过
  • 剩余都有期末考成绩,再看是否有期中成绩,若有看是否高于期末,若是则按比例计算总评,若不是则期末分数即为总评分数
  • 依据四舍五入后的总评将学生信息压入结构体容器,注意检查期中考可能无成绩要登入 -1
  • 对结构体数据进行排序后按标准输出
  • 坑点(最后一个测试点):由于要根据总评是否达到 60 决定这个学生是否会加入到及格容器,所以要在判断前进行四舍五入,而非用 double 型作判断后压入容器才四舍五入,这样会损失部分 [59.5,60) 的学生
#include<iostream>
#include<unordered_map>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
struct Student{
	string id;
	int p, mid, final, grade;
};
bool cmp(Student a, Student b) { return a.grade != b.grade ? a.grade > b.grade : a.id < b.id; }
int main()
{
	int p, m, n, score;
	scanf("%d%d%d", &p, &m, &n);
	unordered_map<string,int> program, mid, final;
	string id;
	for (int i = 0; i < p; i++){
		cin >> id >> score;
		if (score <= 900) program.insert({id,score});
	}
	for (int i = 0; i < m; i++){
		cin >> id >> score;
		if (score <= 100) mid.insert({id,score});
	}
	for (int i = 0; i < n; i++){
		cin >> id >> score;
		if (score <= 100) final.insert({id,score});
	}
	vector<Student> list;
	for (auto it: program){
		string id = it.first;
		if (it.second < 200 || final.find(id) == final.end()) continue;
		if (mid.find(id) != mid.end() && mid[id] > final[id]) score = round(mid[id]*0.4 + final[id]*0.6);
		else score = final[id];
		if (score >= 60) list.push_back({id,it.second,mid.find(id)==mid.end()?-1:mid[id],final[id],score});
	}
	sort(list.begin(), list.end(), cmp);
	for (auto it: list) printf("%s %d %d %d %d\n", it.id.c_str(), it.p,it.mid,it.final,it.grade);
	return 0;
}

1141 PAT Ranking of Institutions (25)

题目思路

  • 用两个 map 分别记录每个学校对应的参考人数和总分
  • map 的 key 值不重复,不需要单独开 set 记录学校 id,只要遍历两个 map 中的一个,将相关信息放到结构体容器中即可
  • 用 sort 对容器排序,由于排名只需要输出不需要记录,可以不记录结构体中而是用变量 rank 记录并输出即可
  • 坑点:由于不同等级分数换算,总分应当用 double 而非 int 来存,全部计算求和完毕后,压入容器排序前再转换为 int
#include<iostream>
#include<unordered_map>
#include<vector>
#include<algorithm>
using namespace std;
struct School{
	string ID;
	int TWS, Ns;
};
bool cmp (School a, School b){
	return a.TWS != b.TWS ? a.TWS > b.TWS : a.Ns != b.Ns ? a.Ns < b.Ns : a.ID < b.ID;
}
int main()
{
	unordered_map<string,int> ns;
	unordered_map<string,double> tws;
	string id, school;
	int n, score, rank = 1;
	scanf("%d\n", &n);
	for (int i = 0; i < n; i++){
		cin >> id >> score >> school;
		transform(school.begin(), school.end(), school.begin(), ::tolower);
		ns[school]++;
		if (id[0] == 'T') tws[school] += score * 1.5;
		else if (id[0] == 'A') tws[school] += score;
		else tws[school] += score / 1.5;
	}
	vector<School> ranklist;
	for (auto it = ns.begin(); it != ns.end(); it++){
		ranklist.push_back({it->first, (int)tws[it->first], it->second});
	}
	sort(ranklist.begin(), ranklist.end(), cmp);
	printf("%d\n%d %s %d %d\n", ranklist.size(), rank, (ranklist[0].ID).c_str(), ranklist[0].TWS, ranklist[0].Ns);
	for (int i = 1; i < ranklist.size(); i++){
		if (ranklist[i].TWS != ranklist[i-1].TWS) rank = i + 1;
		printf("%d %s %d %d\n", rank, (ranklist[i].ID).c_str(), ranklist[i].TWS, ranklist[i].Ns);
	}
	return 0;
}
  • transform(word.begin(),word.end(),new_word.begin(),op); 要转化为大写,op = ::toupper,因 toupper 是在全局命名空间而非 std 中,所以要加 ::
  • 结构体初始化
    • 顺序初始化:不写构造函数,直接用 {} 按顺序传入参数即可初始化
    • 构造函数初始化:写了构造函数可以用()选择性传参
    • 注意写了构造函数就不可以用顺序初始化了

运行超时

  • 有 map 改为 unordered_map
  • 能用 scanf/printf 的不用 cin/cout
  • 排名只需要输出就不存进结构体,减少一次遍历

1153 Decode Registration Card of PAT (25)

题目思路

  • 将所有考生信息接收到结构体容器中,利用构造函数分别赋值便于后面查询。
  • 第1种 query:直接按分数-准考证号标准将所有级别考生信息一起排序,然后遍历时检查符合 query 输入的 level 就输出
  • 第2种 query:可以在输入时计算好,后面直接查询即可
  • 第3种 query:
    • 先遍历一遍所有考生信息,符合输入的日期就将对应的考场号人数++
    • 所有考生检查完毕后遍历所有考场将考生人数不为0的压入vector
    • 依照考生数-考场号标准进行排序,排序后输出
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct CardNumber{
	string cardnum, date;
	char level;
	int site, testeenum, score;
	CardNumber(string _cardnum, int _score): cardnum(_cardnum), score(_score){
		level = _cardnum[0];
		site = stoi(_cardnum.substr(1,3));
		date = _cardnum.substr(4,6);
		testeenum = stoi(_cardnum.substr(10,3));
	}
};
struct SiteNum{
	int site, num;
	SiteNum(int _site, int _num): site(_site), num(_num) {}
};
bool cmp_card(CardNumber a, CardNumber b){ return a.score != b.score ? a.score > b.score : a.cardnum < b.cardnum; }
bool cmp_site(SiteNum a, SiteNum b){ return a.num != b.num ? a.num > b.num : a.site < b.site; }
int main()
{
	int n, m, score, site_tnum[1000] = {0}, site_tscore[1000] = {0};
	string cardnum;
	scanf("%d%d", &n, &m);
	vector<CardNumber> info;
	for (int i = 0; i < n; i++){
		cin >> cardnum >> score;
		info.push_back(CardNumber(cardnum,score));
		site_tnum[info[i].site]++;
		site_tscore[info[i].site] += score;
	}
	sort(info.begin(), info.end(), cmp_card);
	int type, site;
	char level;
	string date;
	for (int i = 0; i < m; i++){
		scanf("%d ", &type);
		bool isempty = true;
		switch(type){
			case 1: 
				scanf("%c",&level);
				printf("Case %d: %d %c\n", i+1, type, level); 
				for (int j = 0; j < n; j++){
					if (info[j].level == level){
						isempty = false;
						cout << info[j].cardnum << " " << info[j].score << endl;
					}
				}
				if (isempty) printf("NA\n");
				break;
			case 2:
				scanf("%d",&site);
				printf("Case %d: %d %d\n", i+1, type, site); 
				if (!site_tnum[site]) printf("NA\n");
				else printf("%d %d\n", site_tnum[site], site_tscore[site]);
				break;
			case 3:
				cin >> date;
				cout << "Case " << i+1 << ": " << type << " " << date << endl;
				int datesitenum[1000] = {0};
				vector<SiteNum> dsn;
				for (int j = 0; j < n; j++){
					if (info[j].date == date){
						isempty = false;
						datesitenum[info[j].site]++;
					}
				}
				if (isempty) printf("NA\n");
				else{
					for (int j = 0; j < 1000; j++) if (datesitenum[j]) dsn.push_back(SiteNum(j,datesitenum[j]));
					sort(dsn.begin(),dsn.end(),cmp_site);
					for (int j = 0; j < dsn.size(); j++) printf("%d %d\n", dsn[j].site, dsn[j].num);
				}
				break;
		}
	}
	return 0;
}
  • 注意结构体构造函数初始化表写法
  • cmp函数中一个 if-else 语句可转为三目运算
  • scanf("%c") 会接收空白符,故输入 type 后的空格应写入 scanf 语句,否则用 scanf 接收第1种 query 时,char 型的 level 值无法接收
posted @ 2019-09-06 13:17  鲸90830  阅读(404)  评论(0编辑  收藏  举报