PAT甲级题分类汇编——杂项
本文为PAT甲级分类汇编系列文章。
集合、散列、数学、算法,这几类的题目都比较少,放到一起讲。
题号 | 标题 | 分数 | 大意 | 类型 |
1063 | Set Similarity | 25 | 集合相似度 | 集合 |
1067 | Sort with Swap(0, i) | 25 | 通过与0号元素交换来排序 | 数学 |
1068 | Find More Coins | 30 | 子集和问题 | 算法 |
1070 | Mooncake | 25 | 背包问题 | 算法 |
1078 | Hashing | 25 | 散列 | 散列 |
1085 | Perfect Sequence | 25 | 符合约束的最大数列长度 | 集合 |
1092 | To Buy or Not to Buy | 20 | 判断子集 | 集合 |
1093 | Count PAT's | 25 | 数子串 | 数学 |
1063就是 std::set 的使用,1092更水,这两道不做了。
集合-1085:
集合,表示方法多种多样,简单的有用数组表示的并查集,复杂的有基于红黑树的 std::set 。
但我也不知道当时整理的时候怎么把这道题分到集合里来了,但还真挺难说这道题怎么分类的。题目要求从输入数据中找出最大的子列(不一定连续)满足最大值不超过最小值的给定倍数。
实现方法比较简单,先排序,然后对于每个数,找到第一个超过它的倍数的数,迭代器相减一下就是长度。查找要用二分不能用线性,会超时。
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 5 int main() 6 { 7 long num, para; 8 std::cin >> num >> para; 9 std::vector<long> data(num); 10 for (auto& i : data) 11 std::cin >> i; 12 std::sort(data.begin(), data.end()); 13 int max = 0; 14 for (int i = 0; i != num; ++i) 15 { 16 if (i == 0 || data[i] != data[i - 1]) 17 { 18 auto left = data.begin() + i; 19 auto right = data.end(); 20 while (left < right) 21 { 22 auto mid = left + (right - left) / 2; 23 if (*mid > data[i] * para) 24 right = mid; 25 else 26 left = mid + 1; 27 } 28 int length = right - data.begin() - i; 29 if (length > max) 30 max = length; 31 if (right == data.end()) 32 break; 33 } 34 } 35 std::cout << max; 36 }
散列-1078:
纯理论地考察散列知识,用单向平方探测法处理冲突。没什么好说的,就是很标准的散列。数据结构那个题集中有一道hard version,那个真的难。
1 #include <iostream> 2 #include <vector> 3 #include <cmath> 4 5 bool is_prime(int _num) 6 { 7 if (_num == 1) 8 return false; 9 if (_num > 2 && _num % 2 == 0) 10 return false; 11 int end = std::sqrt(_num); 12 for (int i = 3; i <= end; i += 2) 13 if (_num % i == 0) 14 return false; 15 return true; 16 } 17 18 int main(int argc, char const *argv[]) 19 { 20 int m; 21 std::cin >> m; 22 while (!is_prime(m)) 23 ++m; 24 std::vector<bool> hash(m); 25 int n; 26 std::cin >> n; 27 for (int i = 0; i != n; ++i) 28 { 29 try 30 { 31 int t; 32 std::cin >> t; 33 for (int j = 0; j != m; ++j) 34 { 35 int h = (t + j * j) % m; 36 if (hash[h] == 0) 37 { 38 hash[h] = 1; 39 if (i > 0) 40 std::cout << ' '; 41 std::cout << h; 42 throw 0; 43 } 44 } 45 if (i > 0) 46 std::cout << ' '; 47 std::cout << '-'; 48 } 49 catch (...) 50 { 51 ; 52 } 53 } 54 return 0; 55 }
数学-1067:
乍一看,完全没有思路。正常排序哪有这么玩的?显然,这道题不考排序,需要一点数学知识把问题转化一下。
对于一个序列,如果值与位置的集合相等,那么一定可以形成若干个环。这道题中需要考虑的是:一元环,即就在应该在的位置上的元素,以及0处于什么样的环中。
自己编几个数据试一下就会发现,需要交换的次数为:
当0处于0号位置时,序列长度+多元环个数-一元环个数(包括0);
当0号位置不是0时,序列长度+多元环个数-一元环个数-2。
1 #include <iostream> 2 #include <utility> 3 4 int main(int argc, char const *argv[]) 5 { 6 int n; 7 std::cin >> n; 8 std::pair<int, bool> data[n]; 9 int loop = 0; 10 int single = 0; 11 for (auto& i : data) 12 std::cin >> i.first; 13 for (int i = 0; i != n; ++i) 14 { 15 if (data[i].second) 16 continue; 17 data[i].second = 1; 18 if (data[i].first == i) 19 ++single; 20 else 21 { 22 ++loop; 23 int t = data[i].first; 24 while (!data[t].second) 25 data[t].second = 1, t = data[t].first; 26 } 27 } 28 if (data[0].first == 0) 29 std::cout << n + loop - single; 30 else 31 std::cout << n + loop - single - 2; 32 33 return 0; 34 }
数学-1093:
要求数“PAT”的个数。如果一个个数的话肯定超时,毕竟题目中都说了数量很大要取模。
正常的算法也不麻烦。先从左向右遍历,数‘P’的数量,在遇到'A'时记录下来;再从右向左遍历,数'T'的数量,也在遇到'A'时记录下来;最后遍历,对每个'A'计算刚才记录的数的乘积并累加。
#include <iostream> #include <string> #include <vector> int main() { std::string str; std::cin >> str; std::vector<int> count_p(str.size()); std::vector<int> count_t(str.size()); long count = 0; for (int i = 0; i != str.size(); ++i) if (str[i] == 'P') ++count; else if (str[i] == 'A') count_p[i] = count; count = 0; for (int i = str.size() - 1; i >= 0; --i) if (str[i] == 'T') ++count; else if (str[i] == 'A') count_t[i] = count; count = 0; for (int i = 0; i != str.size(); ++i) if (str[i] == 'A') count += count_p[i] * count_t[i]; count %= 1000000007; std::cout << count; }
算法-1070:
贪心算法,在数据结构的Dijkstra中提到过一句,没有系统讲过。但是,这道题的算法相当显然,对每种月饼的单价排序,从高到低选择即可。
1 #include <iostream> 2 #include <iomanip> 3 #include <vector> 4 #include <algorithm> 5 #include <cmath> 6 7 struct Mooncake 8 { 9 double weight; 10 double price; 11 }; 12 13 int main() 14 { 15 int num; 16 double demand; 17 std::cin >> num >> demand; 18 std::vector<Mooncake> product(num); 19 for (auto& p : product) 20 std::cin >> p.weight; 21 for (auto& p : product) 22 std::cin >> p.price; 23 std::sort(product.begin(), product.end(), [](const Mooncake& lhs, const Mooncake& rhs) { 24 return lhs.price / lhs.weight > rhs.price / rhs.weight; 25 }); 26 double total = 0; 27 double profit = 0; 28 for (const auto& p : product) 29 { 30 double weight = p.weight > demand - total ? demand - total : p.weight; 31 total += weight; 32 profit += weight / p.weight * p.price; 33 if (std::abs(total - demand) < 0.001) 34 break; 35 } 36 std::cout.setf(std::ios::fixed); 37 std::cout << std::setprecision(2) << profit; 38 }
算法-1068:
这道题压轴,因为我不会。
因为不会,所以瞎写。先写了个递归实现,对每种硬币的数量枚举,其中递归调用函数。遇到有符合要求的组合时,就抛出异常,将硬币组合作为异常参数。结果两个样例竟然都过了,然而提交的结果是4个accept、1个wrong、2个timeout。毕竟递归了,时间复杂度是指数量级的,不超时才怪呢。
想不出更好的算法,去查了答案。这道题是01背包问题,属于动态规划算法,我还没学到,难怪不会做。而且这道题是01背包问题的特殊版,子集和问题,重量和价值都是数的大小。类似的还有著名的两数和、三数和、四数和等。
核心算法是抄的,来自这篇博客。
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <functional> 5 6 int main() 7 { 8 int num, target; 9 std::cin >> num >> target; 10 std::vector<int> coin(num); 11 for (auto& i : coin) 12 std::cin >> i; 13 std::sort(coin.begin(), coin.end(), std::greater<int>()); 14 std::vector<std::vector<bool>> select(num); 15 for (auto& v : select) 16 v.resize(target + 1); 17 std::vector<int> total(target + 1); 18 for (int i = 0; i != num; ++i) 19 for (int j = target; j >= coin[i]; --j) 20 if (total[j] <= total[j - coin[i]] + coin[i]) 21 { 22 select[i][j] = true; 23 total[j] = total[j - coin[i]] + coin[i]; 24 } 25 if (total[target] != target) 26 { 27 std::cout << "No Solution"; 28 return 0; 29 } 30 int price = target; 31 int which = num - 1; 32 std::vector<int> found; 33 while (price) 34 { 35 if (select[which][price]) 36 { 37 found.push_back(coin[which]); 38 price -= coin[which]; 39 } 40 --which; 41 } 42 int count = 0; 43 for (int i : found) 44 { 45 if (count++) 46 std::cout << ' '; 47 std::cout << i; 48 } 49 }
最后还要说一句,Eva你已经是一个大孩子了,要学会自己解决问题,别总是让我写算法。关键问题是你还老给我出难题。
posted on 2019-09-04 23:36 Jerry_SJTU 阅读(398) 评论(0) 编辑 收藏 举报