本文爲PAT甲級分類彙編系列文章。html
排序題,就是以排序算法爲主的題。純排序,用 std::sort 就能解決的那種,20分都算不上,只能放在乙級,甲級的排序題要麼是排序的規則複雜,要麼是排完序還要作點什麼的。ios
在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 |
選了105六、1075和1080 3道作,其餘不作是由於以爲太水了。app
1056:測試
題目要求模擬晉級賽,每組選手中的最高分進入下一組,同一輪中淘汰的名次相同。spa
邊界狀況是隻剩一我的,這時比賽就結束了,是循環的結束條件,因此也不算邊界的坑了。code
主循環中用到兩個 std::vector<int> 對象,分別做爲當前一輪的選手與晉級的選手,在循環的最後一個賦值一個清空。很是巧的是(也多是必然),輸入數據中的順序恰好能夠表示當前一輪。htm
很簡單的題,一遍就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:blog
這道題要求模擬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次,時間分別是19一、1十、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篇,能夠不用睡覺了。