本文为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. 第10组 Alpha冲刺(3/6)

    链接部分 队名:女生都队 组长博客: 博客链接 作业博客:博客链接 小组内容 恩泽(组长) 过去两天完成了哪些任务 描述 对推送模块进行详细划分 基于用户的协同过滤,寻找更感兴趣的话题 学习API文档 ...

  2. 网络IPC:套接字接口概述

    网络IPC:套接字接口概述 套接字接口实现了通过网络连接的不同计算机之间的进程相互通信的机制. 套接字描述符(创建套接字) 套接字是通信端点的抽象,为创建套接字,调用socket函数 #include ...

  3. PHP如何解决网站大流量与高并发的问题(一)

    高并发的相关概念 在某个时间点,有多少个访问量 如果一个系统的日PV在千万以上,有可能是一个高并发的系统 QPS: 每秒钟请求或者查询的数量,在互联网领域,指每秒相应请求数(指HTTP请求) 吞吐量: ...

  4. Chrome 9229

    Enable Inspector When started with the --inspect switch, a Node.js process listens for a debugging c ...

  5. [Web前端] WEEX、React-Native开发App心得 (转载)

    转自: https://www.jianshu.com/p/139c5074ae5d 2018 JS状态报告: https://2018.stateofjs.com/mobile-and-deskto ...

  6. JVM 初始化阶段例子 final常量

    1.创建FinalTest类,里面有一个final常量x class FinalTest{ public static final int x = 3; static { System.out.pri ...

  7. 【Java/Json】Java对Json进行建模,分词,递归向下解析构建Json对象树

    伸手党的福音 代码下载:https://files.cnblogs.com/files/xiandedanteng/JsonLexerBuilder20191202.rar 互联网上成型的对Json进 ...

  8. Windows安装Redis并添加本地自启动服务并解决客户端dll报错

    参考文章:https://blog.csdn.net/realjh/article/details/82026160 Redis下载: https://github.com/MicrosoftArch ...

  9. shell编程系列14--文本处理三剑客之awk的概述及常用方法总结

    shell编程系列14--文本处理三剑客之awk的概述及常用方法总结 awk是一个文本处理工具,通常用于处理数据并生成结果报告 awk的命名是它的创始人 Alfred Aho.Peter Weinbe ...

  10. 修复gitlab服务器突然停电导致PostgreSQL损坏的数据库

    最开始是存储的卷组受损,使用的DRBD,使用了xfs分区格式: 挂载也报错: mount /dev/drbd0 /var/opt mount: wrong fs type, bad option, b ...