本文为PAT甲级分类汇编系列文章。

排序题,就是以排序算法为主的题。纯排序,用 std::sort 就能解决的那种,20分都算不上,只能放在乙级,甲级的排序题要么是排序的规则复杂,要么是排完序还要做点什么的。

在1051至1100中有6道:

题号 标题 分数 大意 时间
1055 The World's Richest 25 限定范围排序结果 500ms
1056 Mice and Rice 25 分组排序 200ms
1062 Talent and Virtue 25 一定规则的排序 400ms
1075 PAT Judge 25 复杂排序 200ms
1080 Graduate Admission 30 志愿与录取 250ms
1083 List Grades 25 限定范围排序结果 400ms

选了1056、1075和1080 3道做,其他不做是因为觉得太水了。

1056:

题目要求模拟晋级赛,每组选手中的最高分进入下一组,同一轮中淘汰的名次相同。

边界情况是只剩一个人,这时比赛就结束了,是循环的结束条件,所以也不算边界的坑了。

主循环中用到两个 std::vector<int> 对象,分别作为当前一轮的选手与晋级的选手,在循环的最后一个赋值一个清空。非常巧的是(也可能是必然),输入数据中的顺序刚好可以表示当前一轮。

很简单的题,一遍就AC了。

 #include <iostream>
#include <vector>
#include <algorithm> struct Programmer
{
int index;
int mice;
int score;
int rank;
}; int main(int argc, char const *argv[])
{
int total, per;
std::cin >> total >> per;
std::vector<Programmer> prog(total);
for (int i = ; i != total; ++i)
prog[i].index = i, std::cin >> prog[i].mice;
std::vector<int> current(total);
for (int i = ; i != total; ++i)
std::cin >> current[i]; std::vector<int> next;
while ()
{
auto iter = current.begin();
int index;
while (iter != current.end())
{
int max = -;
for (int i = ; i != per && iter != current.end(); ++i, ++iter)
if (prog[*iter].mice > max)
{
index = *iter;
max = prog[*iter].mice;
}
++prog[index].score;
next.push_back(index);
}
if (next.size() == )
break;
current = next;
next.clear();
} std::sort(prog.begin(), prog.end(), [](const Programmer& lhs, const Programmer& rhs) {
return lhs.score > rhs.score;
});
int count = ;
prog.front().rank = ;
for (auto iter = prog.begin() + ; iter != prog.end(); ++iter, ++count)
if (iter->score == (iter - )->score)
iter->rank = (iter - )->rank;
else
iter->rank = count;
std::sort(prog.begin(), prog.end(), [](const Programmer& lhs, const Programmer& rhs) {
return lhs.index < rhs.index;
});
auto end = prog.end() - ;
for (auto iter = prog.begin(); iter != end; ++iter)
std::cout << iter->rank << ' ';
std::cout << end->rank << std::endl; return ;
}

1075:

这道题要求模拟PAT评分系统,统计一系列提交,最后按照用户来输出。麻烦的是没有提交、编译错误、满分等情况,之前看到这道题的时候就觉得太烦跳了。

要排好序,主要要搞清楚题目里的这些概念:总分、满分题数、有效用户,还有提交与输出分数的关系。

一个个来讲吧。总分就是所有分数加起来,这很简单。然而,由于-1表示编译错误的存在,累加不能直接相加,要判断是否大于0(大于等于也一样)。同时还需要一种表示没有提交过的方法,我就用-2了。

满分题数,就是把一个人的各题分数一个个和满分比较,所有相等的数量。这是排序中的第二关键字。

有效用户,就是有过编译通过的提交的用户。就算提交完是0分,也算有效用户,这个点坑到了。

提交与分数,坑在如果提交编译错误,这道题是算0分而不是算没提交,这个也坑到了。

区区一道25分题就放那么多坑,我下午放学开始写,晚上熄灯后才写完(虽然没有一直在写,至少加起来也一个多小时了,但主要是不AC我难受啊),姥姥你心不痛吗?

 #include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm> int num_problem;
std::vector<int> problem_full; class User
{
public:
User(int _id)
: id_(_id), problems(num_problem, -)
{
;
}
void submission(int _pro, int _score)
{
if (_score > problems[_pro])
problems[_pro] = _score;
}
bool operator<(const User& _user) const
{
calculate();
_user.calculate();
if (score_ > _user.score_)
return true;
if (score_ < _user.score_)
return false;
if (perfect_ > _user.perfect_)
return true;
if (perfect_ < _user.perfect_)
return false;
return id_ < _user.id_;
}
bool valid() const
{
calculate();
return valid_;
}
void rank(int _rank)
{
rank_ = _rank;
}
int rank() const
{
return rank_;
}
int score() const
{
calculate();
return score_;
}
friend std::ostream& operator<<(std::ostream& _os, const User& _user);
private:
int id_;
std::vector<int> problems;
mutable bool calculated_;
mutable int score_;
mutable int perfect_;
mutable bool valid_;
int rank_;
void calculate() const
{
if (!calculated_)
{
calculated_ = true;
for (int i = ; i != problems.size(); ++i)
if (problems[i] >= )
{
score_ += problems[i];
if (problems[i] == problem_full[i])
++perfect_;
valid_ = true;
}
}
}
}; std::ostream& operator<<(std::ostream& _os, const User& _user)
{
std::cout << std::setfill('');
_os << _user.rank_ << ' ';
_os << std::setw() << _user.id_ << ' ';
_os << _user.score_;
for (int s : _user.problems)
{
std::cout << ' ';
if (s >= )
std::cout << s;
else if (s == -)
std::cout << '';
else
std::cout << '-';
}
return _os;
} int main(int argc, char const *argv[])
{
int num_user;
int num_submission;
std::cin >> num_user >> num_problem >> num_submission; std::vector<User> users;
users.reserve(num_user);
for (int i = ; i != num_user; ++i)
{
users.emplace_back(i + );
}
problem_full.reserve(num_problem);
for (int i = ; i != num_problem; ++i)
{
int t;
std::cin >> t;
problem_full.push_back(t);
}
for (int i = ; i != num_submission; ++i)
{
int user, pro, score;
std::cin >> user >> pro >> score;
--user;
--pro;
users[user].submission(pro, score);
}
std::sort(users.begin(), users.end());
int count = ;
users.front().rank(count++);
for (auto iter = users.begin() + ; iter != users.end(); ++iter, ++count)
{
if (iter->score() == (iter - )->score())
iter->rank((iter - )->rank());
else
iter->rank(count);
}
for (const auto& u : users)
if (u.valid())
std::cout << u << std::endl;
else
break; return ;
}

为了优雅,我把代码写得很OO(其实是object-based)。虽然也没有人会去复用它。

还有一点,测试数据里的case 4很诡异,我提交了3次,时间分别是191、110、194ms。更关键的是这道题限制就200ms,我要是再写烂一点不就超时了吗?还一会超时一会不超时的,搞不懂。

1080:

这道题要求模拟志愿录取,核心算法在于分配而不是排序,之前读题的时候没注意,给分到这里来了。

输入、排序、输出都很简单,难在分配,即所谓录取过程。要是没有并列的都要录取这条规则,对排序完的学生遍历一遍就可以了,但它偏要有这条,不过谁让它是30分题呢。讲真,我感觉这道30分比上一道25分简单,一遍AC。

我解决并列问题的方法是这样的:一对iterator,分别指向并列一段的起始和尾后,然后将学校名额“锁住”,保持它是否有空余名额的状态,这时往里塞。就算超名额也不管,是题目要求的。“锁住”以后也不用“解锁”,下次“锁住”的时候会更新状态(也许应该换个名字,毕竟lock以后不unlock怪怪的)。

 #include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <utility>
using std::rel_ops::operator>; struct School
{
int quota;
std::vector<int> admitted;
bool free()
{
return free_;
}
void lock()
{
free_ = admitted.size() < quota;
}
private:
bool free_;
}; struct Student
{
int id;
int grade_e;
int grade_i;
std::vector<int> choices;
int rank;
bool operator<(const Student& _rhs) const
{
if (grade_e + grade_i < _rhs.grade_e + _rhs.grade_i)
return true;
if (grade_e + grade_i > _rhs.grade_e + _rhs.grade_i)
return false;
return grade_e < _rhs.grade_e;
}
bool operator==(const Student& _rhs) const
{
return grade_e == _rhs.grade_e && grade_i == _rhs.grade_i;
}
}; int main()
{
int num_applicant, num_school, num_choice;
std::cin >> num_applicant >> num_school >> num_choice;
std::vector<School> schools(num_school);
std::vector<Student> students(num_applicant);
for (auto& s : schools)
std::cin >> s.quota;
for (int i = ; i != num_applicant; ++i)
{
auto& s = students[i];
s.id = i;
std::cin >> s.grade_e >> s.grade_i;
s.choices.resize(num_choice);
for (auto& i : s.choices)
std::cin >> i;
}
std::sort(students.begin(), students.end(), std::greater<Student>());
for (auto iter = students.begin() + ; iter != students.end(); ++iter)
if (*iter == *(iter - ))
iter->rank = (iter - )->rank;
else
iter->rank = (iter - )->rank + ;
auto end = students.begin();
while (end != students.end())
{
auto iter = end;
while (end != students.end() && *end == *iter)
++end;
for (auto& s : schools)
s.lock();
for (; iter != end; ++iter)
{
for (const auto& s : iter->choices)
if (schools[s].free())
{
schools[s].admitted.push_back(iter->id);
break;
}
}
}
for (auto& s : schools)
{
std::sort(s.admitted.begin(), s.admitted.end());
if (!s.admitted.empty())
{
auto end = s.admitted.end() - ;
for (auto iter = s.admitted.begin(); iter != end; ++iter)
std::cout << *iter << ' ';
std::cout << *end;
}
std::cout << std::endl;
}
}

自己用 operator< 来实现 operator> 太烦了,我选择 std::rel_ops 。

还有一个星期就要考了,我10篇才写了3篇,可以不用睡觉了。

PAT甲级题分类汇编——排序的更多相关文章

  1. PAT甲级题分类汇编——杂项

    本文为PAT甲级分类汇编系列文章. 集合.散列.数学.算法,这几类的题目都比较少,放到一起讲. 题号 标题 分数 大意 类型 1063 Set Similarity 25 集合相似度 集合 1067 ...

  2. PAT甲级题分类汇编——图

    本文为PAT甲级分类汇编系列文章. 图,就是层序遍历和Dijkstra这一套,#include<queue> 是必须的. 题号 标题 分数 大意 时间 1072 Gas Station 3 ...

  3. PAT甲级题分类汇编——理论

    本文为PAT甲级分类汇编系列文章. 理论这一类,是让我觉得特别尴尬的题,纯粹是为了考数据结构而考数据结构.看那Author一栏清一色的某老师,就知道教数据结构的老师的思路就是和别人不一样. 题号 标题 ...

  4. PAT甲级题分类汇编——线性

    本文为PAT甲级分类汇编系列文章. 线性类,指线性时间复杂度可以完成的题.在1051到1100中,有7道: 题号 标题 分数 大意 时间 1054 The Dominant Color 20 寻找出现 ...

  5. PAT甲级题分类汇编——树

    本文为PAT甲级分类汇编系列文章. AVL树好难!(其实还好啦~) 我本来想着今天应该做不完树了,没想到电脑里有一份讲义,PPT和源代码都有,就一遍复习一遍抄码了一遍,更没想到的是编译一遍通过,再没想 ...

  6. PAT甲级题分类汇编——计算

    本文为PAT甲级分类汇编系列文章. 计算类,指以数学运算为主或为背景的题. 题号 标题 分数 大意 1058 A+B in Hogwarts 20 特殊进制加法 1059 Prime Factors ...

  7. PAT甲级题分类汇编——序言

    今天开个坑,分类整理PAT甲级题目(https://pintia.cn/problem-sets/994805342720868352/problems/type/7)中1051~1100部分.语言是 ...

  8. 【转载】【PAT】PAT甲级题型分类整理

    最短路径 Emergency (25)-PAT甲级真题(Dijkstra算法) Public Bike Management (30)-PAT甲级真题(Dijkstra + DFS) Travel P ...

  9. PAT甲级1017题解——模拟排序

    题目分析: 本题我第一次尝试去做的时候用的是优先队列,但是效率不仅代码量很大,而且还有测试样例过不去,很显然没有找到一个好的数据结构来解决这道题目(随着逐渐的刷PAT甲级的题会发现有时选择一个好的解题 ...

随机推荐

  1. 【Java文件】按UTF-8编码读取文本文件(逐行方式),排序,打印到控制台

    代码: package findJavaMemberFunction; import java.io.BufferedReader; import java.io.FileInputStream; i ...

  2. 【MySQL】Mac通过brew安装的MySQL重启方法

    问题 在 Mac 上通过 brew install mysql 安装的的MySQL使用基本MySQL命令重启会失败: mysql.server stop mysql.server start mysq ...

  3. Docs-.NET-C#-指南-语言参考-预处理器指令:#pragma warning(C# 参考)

    ylbtech-Docs-.NET-C#-指南-语言参考-预处理器指令:#pragma warning(C# 参考) 1.返回顶部 1. #pragma warning(C# 参考) 2015/07/ ...

  4. dig命令不能使用(-bash: dig: command not found)

    解决方式: 直接使用yum进行安装: yum -y install bind-utils

  5. PHP 小程序发模板消息

    记录一下DEMO <?php function getAccessToken ($appid, $appsecret) { $url='https://api.weixin.qq.com/cgi ...

  6. HTTP1.1新增了五种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 、 CONNECT

    200 (成功) 服务器已成功处理了请求. 通常,这表示服务器提供了请求的网页. 201 (已创建) 请求成功并且服务器创建了新的资源. 202 (已接受) 服务器已接受请求,但尚未处理. 203 ( ...

  7. Laya的场景以及场景的加载

    参考: Laya项目发布详解 Laya2.0 内嵌模式.加载模式.分离模式.文件模式的场景加载创建和场景打开关闭 版本2.1.1.1 白鹭中的场景是exml制作,发布后exml代码都会打包到defau ...

  8. PP篇9 更改计划订单

    首先看了下这个函数的版本  还挺新的 S4版本应该是有更新过,里面用了新语法 导入: PLANNEDORDER    计划订单号HEADERDATA          表头数据(来自外部系统) HEA ...

  9. Spring Boot应用的打包和部署

    传统的Web应用在发布之前通常会打成WAR包,然后将WAR包部署到Tomcat等容器中使用,而通过前面的学习我们已经知道,Spring Boot应用既能以JAR包的形式部署,又能以WAR包的形式部署. ...

  10. php环境选择

    第一个    PHPStudy 推荐这个.简单好用. 链接:https://pan.baidu.com/s/1yWRDjfnadkkUE-JX5pqZmg 提取码:4imw 第二个 PHPnow 第三 ...