PAT甲级题分类汇编——排序
本文为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甲级题分类汇编——排序的更多相关文章
- PAT甲级题分类汇编——杂项
本文为PAT甲级分类汇编系列文章. 集合.散列.数学.算法,这几类的题目都比较少,放到一起讲. 题号 标题 分数 大意 类型 1063 Set Similarity 25 集合相似度 集合 1067 ...
- PAT甲级题分类汇编——图
本文为PAT甲级分类汇编系列文章. 图,就是层序遍历和Dijkstra这一套,#include<queue> 是必须的. 题号 标题 分数 大意 时间 1072 Gas Station 3 ...
- PAT甲级题分类汇编——理论
本文为PAT甲级分类汇编系列文章. 理论这一类,是让我觉得特别尴尬的题,纯粹是为了考数据结构而考数据结构.看那Author一栏清一色的某老师,就知道教数据结构的老师的思路就是和别人不一样. 题号 标题 ...
- PAT甲级题分类汇编——线性
本文为PAT甲级分类汇编系列文章. 线性类,指线性时间复杂度可以完成的题.在1051到1100中,有7道: 题号 标题 分数 大意 时间 1054 The Dominant Color 20 寻找出现 ...
- PAT甲级题分类汇编——树
本文为PAT甲级分类汇编系列文章. AVL树好难!(其实还好啦~) 我本来想着今天应该做不完树了,没想到电脑里有一份讲义,PPT和源代码都有,就一遍复习一遍抄码了一遍,更没想到的是编译一遍通过,再没想 ...
- PAT甲级题分类汇编——计算
本文为PAT甲级分类汇编系列文章. 计算类,指以数学运算为主或为背景的题. 题号 标题 分数 大意 1058 A+B in Hogwarts 20 特殊进制加法 1059 Prime Factors ...
- PAT甲级题分类汇编——序言
今天开个坑,分类整理PAT甲级题目(https://pintia.cn/problem-sets/994805342720868352/problems/type/7)中1051~1100部分.语言是 ...
- 【转载】【PAT】PAT甲级题型分类整理
最短路径 Emergency (25)-PAT甲级真题(Dijkstra算法) Public Bike Management (30)-PAT甲级真题(Dijkstra + DFS) Travel P ...
- PAT甲级1017题解——模拟排序
题目分析: 本题我第一次尝试去做的时候用的是优先队列,但是效率不仅代码量很大,而且还有测试样例过不去,很显然没有找到一个好的数据结构来解决这道题目(随着逐渐的刷PAT甲级的题会发现有时选择一个好的解题 ...
随机推荐
- 2019SDN课程阅读作业(2)
1.过去20年中可编程网络的发展可以分为几个阶段?每个阶段的贡献是什么? 分为三个阶段,第一个阶段是主动网络(从20世纪90年代中期到21世纪初),它在网络中引入了可编程功能,以实现更大的创新:第二个 ...
- (基因功能 & 基因表达调控)研究方案
做了好久的RNA-seq分析,基因表达也在口头溜了几年了,但似乎老是浮在表面. 对一件事的了解程度决定了你的思维深度,只想做技工就不用想太多,想做大师就一定要刨根问底. 老是说基因表达,那么什么是基因 ...
- 交互式报告系统 Dr. Tom | 华大基因培训资料
华大科技服务开发一套优秀的交互式结题报告系统,适用于没有代码基础的老师分析自己的数据. http://report.bgi.com/ps/login/login.html 体验之后再做评价! 见云盘: ...
- PCA python 实现
PCA 实现: 参考博客:https://blog.csdn.net/u013719780/article/details/78352262 from __future__ import print_ ...
- 沃顿商学院的MBA课程
沃顿商学院的MBA课程,分为必修课和选修课两部分 (一)必修课: 1.领导力:团队合作和领导力的基础 2.营销学:营销管理 3.微观经济学:微观经济基础 4.经济学:管理经济学的高级话题 5.统计学: ...
- TypedValue: 使用TypedValue将dip值转换成px值
之前自己一直手工编写函数来实现dip值到px值,今天无意中发现android本身就带有类似的函数来实现这种转换过程,就是 TypedValue.applyDimension public class ...
- c# 验证
public class RegularExpressionsHelper { /// <summary> /// 对用户名进行格式进行检查的正则表达式 /// </summary& ...
- typeScript模块<一>
/*模块 模块的的概念 模块导出的几种方法 1.export 导出声明 2.export 导出语句 3.export default 4.import导入模块 模块化封装上一讲的DB库 */ /* 模 ...
- ISO/IEC 9899:2011 条款6.2——概念
6.2 概念 6.2.1 标识符的作用域 6.2.2 标识符的连接 6.2.3 标识符的名字空间 6.2.4 对象的存储持久性 6.2.5 类型 6.2.6 类型的表示 6.2.7 兼容类型与组合类型 ...
- ES6深入浅出-3 三个点运算 & 新版字符串-1.函数与对象的语法糖
主要讲的内容 时间充裕的话就讲,模板字面量 默认参数值 首先讲es6之前,我们是怎么做的.例如我们要写一个求和的函数, 请两个参数的和,但是如果有的人就是穿一个参数呢? 那么b没有传值,b的值是多少呢 ...