Jerry @DOA&INPAC, SJTU

Working out everything from the first principles.

导航

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了。

 1 #include <iostream>
 2 #include <vector>
 3 #include <algorithm>
 4 
 5 struct Programmer
 6 {
 7     int index;
 8     int mice;
 9     int score;
10     int rank;
11 };
12 
13 
14 int main(int argc, char const *argv[])
15 {
16     int total, per;
17     std::cin >> total >> per;
18     std::vector<Programmer> prog(total);
19     for (int i = 0; i != total; ++i)
20         prog[i].index = i, std::cin >> prog[i].mice;
21     std::vector<int> current(total);
22     for (int i = 0; i != total; ++i)
23         std::cin >> current[i];
24     
25     std::vector<int> next;
26     while (1)
27     {
28         auto iter = current.begin();
29         int index;
30         while (iter != current.end())
31         {
32             int max = -1;
33             for (int i = 0; i != per && iter != current.end(); ++i, ++iter)
34                 if (prog[*iter].mice > max)
35                 {
36                     index = *iter;
37                     max = prog[*iter].mice;
38                 }
39             ++prog[index].score;
40             next.push_back(index);
41         }
42         if (next.size() == 1)
43             break;
44         current = next;
45         next.clear();
46     }
47 
48     std::sort(prog.begin(), prog.end(), [](const Programmer& lhs, const Programmer& rhs) {
49         return lhs.score > rhs.score;
50     });
51     int count = 2;
52     prog.front().rank = 1;
53     for (auto iter = prog.begin() + 1; iter != prog.end(); ++iter, ++count)
54         if (iter->score == (iter - 1)->score)
55             iter->rank = (iter - 1)->rank;
56         else
57             iter->rank = count;
58     std::sort(prog.begin(), prog.end(), [](const Programmer& lhs, const Programmer& rhs) {
59         return lhs.index < rhs.index;
60     });
61     auto end = prog.end() - 1;
62     for (auto iter = prog.begin(); iter != end; ++iter)
63         std::cout << iter->rank << ' ';
64     std::cout << end->rank << std::endl;
65 
66     return 0;
67 }

 

1075:

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

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

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

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

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

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

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

  1 #include <iostream>
  2 #include <iomanip>
  3 #include <vector>
  4 #include <algorithm>
  5 
  6 int num_problem;
  7 std::vector<int> problem_full;
  8 
  9 class User
 10 {
 11 public:
 12     User(int _id)
 13         : id_(_id), problems(num_problem, -2)
 14     {
 15         ;
 16     }
 17     void submission(int _pro, int _score)
 18     {
 19         if (_score > problems[_pro])
 20             problems[_pro] = _score;
 21     }
 22     bool operator<(const User& _user) const
 23     {
 24         calculate();
 25         _user.calculate();
 26         if (score_ > _user.score_)
 27             return true;
 28         if (score_ < _user.score_)
 29             return false;
 30         if (perfect_ > _user.perfect_)
 31             return true;
 32         if (perfect_ < _user.perfect_)
 33             return false;
 34         return id_ < _user.id_;
 35     }
 36     bool valid() const
 37     {
 38         calculate();
 39         return valid_;
 40     }
 41     void rank(int _rank)
 42     {
 43         rank_ = _rank;
 44     }
 45     int rank() const
 46     {
 47         return rank_;
 48     }
 49     int score() const
 50     {
 51         calculate();
 52         return score_;
 53     }
 54     friend std::ostream& operator<<(std::ostream& _os, const User& _user);
 55 private:
 56     int id_;
 57     std::vector<int> problems;
 58     mutable bool calculated_;
 59     mutable int score_;
 60     mutable int perfect_;
 61     mutable bool valid_;
 62     int rank_;
 63     void calculate() const
 64     {
 65         if (!calculated_)
 66         {
 67             calculated_ = true;
 68             for (int i = 0; i != problems.size(); ++i)
 69                 if (problems[i] >= 0)
 70                 {
 71                     score_ += problems[i];
 72                     if (problems[i] == problem_full[i])
 73                         ++perfect_;
 74                     valid_ = true;
 75                 }
 76         }
 77     }
 78 };
 79 
 80 std::ostream& operator<<(std::ostream& _os, const User& _user)
 81 {
 82     std::cout << std::setfill('0');
 83     _os << _user.rank_ << ' ';
 84     _os << std::setw(5) << _user.id_ << ' ';
 85     _os << _user.score_;
 86     for (int s : _user.problems)
 87     {
 88         std::cout << ' ';
 89         if (s >= 0)
 90             std::cout << s;
 91         else if (s == -1)
 92             std::cout << '0';
 93         else
 94             std::cout << '-';
 95     }
 96     return _os;
 97 }
 98 
 99 int main(int argc, char const *argv[])
100 {
101     int num_user;
102     int num_submission;
103     std::cin >> num_user >> num_problem >> num_submission;
104 
105     std::vector<User> users;
106     users.reserve(num_user);
107     for (int i = 0; i != num_user; ++i)
108     {
109         users.emplace_back(i + 1);
110     }
111     problem_full.reserve(num_problem);
112     for (int i = 0; i != num_problem; ++i)
113     {
114         int t;
115         std::cin >> t;
116         problem_full.push_back(t);
117     }
118     for (int i = 0; i != num_submission; ++i)
119     {
120         int user, pro, score;
121         std::cin >> user >> pro >> score;
122         --user;
123         --pro;
124         users[user].submission(pro, score);
125     }
126     std::sort(users.begin(), users.end());
127     int count = 1;
128     users.front().rank(count++);
129     for (auto iter = users.begin() + 1; iter != users.end(); ++iter, ++count)
130     {
131         if (iter->score() == (iter - 1)->score())
132             iter->rank((iter - 1)->rank());
133         else
134             iter->rank(count);
135     }
136     for (const auto& u : users)
137         if (u.valid())
138             std::cout << u << std::endl;
139         else
140             break;
141 
142     return 0;
143 }

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

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

 

1080:

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

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

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

 1 #include <iostream>
 2 #include <vector>
 3 #include <algorithm>
 4 #include <functional>
 5 #include <utility>
 6 using std::rel_ops::operator>;
 7 
 8 struct School
 9 {
10     int quota;
11     std::vector<int> admitted;
12     bool free()
13     {
14         return free_;
15     }
16     void lock()
17     {
18         free_ = admitted.size() < quota;
19     }
20 private:
21     bool free_;
22 };
23 
24 struct Student
25 {
26     int id;
27     int grade_e;
28     int grade_i;
29     std::vector<int> choices;
30     int rank;
31     bool operator<(const Student& _rhs) const
32     {
33         if (grade_e + grade_i < _rhs.grade_e + _rhs.grade_i)
34             return true;
35         if (grade_e + grade_i > _rhs.grade_e + _rhs.grade_i)
36             return false;
37         return grade_e < _rhs.grade_e;
38     }
39     bool operator==(const Student& _rhs) const
40     {
41         return grade_e == _rhs.grade_e && grade_i == _rhs.grade_i;
42     }
43 };
44 
45 int main()
46 {
47     int num_applicant, num_school, num_choice;
48     std::cin >> num_applicant >> num_school >> num_choice;
49     std::vector<School> schools(num_school);
50     std::vector<Student> students(num_applicant);
51     for (auto& s : schools)
52         std::cin >> s.quota;
53     for (int i = 0; i != num_applicant; ++i)
54     {
55         auto& s = students[i];
56         s.id = i;
57         std::cin >> s.grade_e >> s.grade_i;
58         s.choices.resize(num_choice);
59         for (auto& i : s.choices)
60             std::cin >> i;
61     }
62     std::sort(students.begin(), students.end(), std::greater<Student>());
63     for (auto iter = students.begin() + 1; iter != students.end(); ++iter)
64         if (*iter == *(iter - 1))
65             iter->rank = (iter - 1)->rank;
66         else
67             iter->rank = (iter - 1)->rank + 1;
68     auto end = students.begin();
69     while (end != students.end())
70     {
71         auto iter = end;
72         while (end != students.end() && *end == *iter)
73             ++end;
74         for (auto& s : schools)
75             s.lock();
76         for (; iter != end; ++iter)
77         {
78             for (const auto& s : iter->choices)
79                 if (schools[s].free())
80                 {
81                     schools[s].admitted.push_back(iter->id);
82                     break;
83                 }
84         }
85     }
86     for (auto& s : schools)
87     {
88         std::sort(s.admitted.begin(), s.admitted.end());
89         if (!s.admitted.empty())
90         {
91             auto end = s.admitted.end() - 1;
92             for (auto iter = s.admitted.begin(); iter != end; ++iter)
93                 std::cout << *iter << ' ';
94             std::cout << *end;
95         }
96         std::cout << std::endl;
97     }
98 }

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

 

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

 

posted on 2019-09-01 23:47  Jerry_SJTU  阅读(580)  评论(0编辑  收藏  举报