本文为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. LCT 总结

    刚开始学lct花了一晚上研究模板,调出来就感觉不怎么难打了. 对板子的浅显理解: lct维护树形联通块,通过splay维护实链,可以把需要的路径变换到一颗splay上维护. splay中的关系只依赖实 ...

  2. appium+python 微信小程序的自动化

    sudo kill -9 $(lsof -i:8889 -t) mitmweb -p 8889 -s addons.py mitmdump -q  -p 8889 -s addons.py http: ...

  3. MITMProxy如何配置二次代理

    MITMProxy如何配置二次代理 0.2172018.09.05 11:13:15字数 232阅读 2609 前序: mitmproxy真的很强大,或许是大家都各自使用,或者没有相关的需求,导致我废 ...

  4. QT文本转语音模块(TTS)QTextToSpeech

    QTextToSpeech是QT5.8以上的文本转语音模块. 使用方法: 在.pro工程文件中添加“QT += texttospeech”. 添加头文件 #include <QTextToSpe ...

  5. Apollo的基本使用及常见问题

    1. 创建项目 在创建项目页面中填写相关项目信息,最后点击提交即可创建项目. 注意:应用Id必须唯一并且与客户配置的app.id一致. 2. 发布 进入对应项目可通过文本(批量)或者表格模式添加配置, ...

  6. QEMU命令配置虚拟机网络的用户模式

    QEMU缺省使用“-net nic-net user”参数为客户机配置网络,提供了一种用户模式( user-mode)的网络模拟.使用用户模式的客户机可以连通宿主机及外部网络.用户模式网络完全由QEM ...

  7. K8S集群Master高可用实践

    K8S集群Master高可用实践    https://blog.51cto.com/ylw6006/2164981 本文将在前文基础上介绍k8s集群的高可用实践,一般来讲,k8s集群高可用主要包含以 ...

  8. 【转载】 KL距离(相对熵)

    原文地址: https://www.cnblogs.com/nlpowen/p/3620470.html ----------------------------------------------- ...

  9. linux安装Erlang

    Erlang一种通用的面向并发的编程语言. 1.安装Erlang编译依赖: yum -y install gcc glibc-devel make ncurses-devel openssl-deve ...

  10. 零基础学Python-第一章 :Python介绍和安装-02.Python的发展历史与版本

    pthhon3.0对开发者带来了麻烦,因为2.0个3.0的程序并不是兼容的. 目前python3.0已经变成了真正的主力 官方版本和发行版 发行版会把常用的科学计算的包也给继承进来.这样就不用考虑包和 ...