PAT(甲级)2020年春季考试
7-1 Prime Day (20 分)
时间限制:400 ms 内存限制:64 MB
The above picture is from Sina Weibo, showing May 23rd, 2019 as a very cool "Prime Day". That is, not only that the corresponding number of the date 20190523
is a prime, but all its sub-strings ended at the last digit 3
are prime numbers.
Now your job is to tell if a given date is a Prime Day.
Input Specification:
Each input file contains one test case. For each case, a date between January 1st, 0001 and December 31st, 9999 is given, in the format yyyymmdd
.
Output Specification:
For each given date, output in the decreasing order of the length of the substrings, each occupies a line. In each line, print the string first, followed by a space, then Yes
if it is a prime number, or No
if not. If this date is a Prime Day, print in the last line All Prime!
.
Sample Input 1:
20190523
Sample Output 1:
20190523 Yes
0190523 Yes
190523 Yes
90523 Yes
0523 Yes
523 Yes
23 Yes
3 Yes
All Prime!
Sample Input 2:
20191231
Sample Output 2:
20191231 Yes
0191231 Yes
191231 Yes
91231 No
1231 Yes
231 No
31 Yes
1 No
解析:
简单的质数判断+字符串截取。字符串转int用stoi就可以了。
#include <cmath>
#include <iostream>
using namespace std;
bool isPrime (int number) {
if (number <= 1) return false;
int sqr = (int)sqrt(number), i;
for (i = 2; i <= sqr; i++) {
if (number % i == 0) return false;
}
return true;
}
void test() {
string s;
cin >> s;
int flag = 0;
for (int i = 0; i < s.length(); i++) {
string t1 = s.substr(i, s.length());
int t2 = stoi(t1);
printf("%s ", t1.c_str());
if (isPrime(t2)) printf("Yes\n");
else {
printf("No\n");
flag = 1;
}
}
if (flag == 0) printf("All Prime!");
}
int main(){
test();
return 0;
}
7-2 The Judger (25 分)
时间限制:400 ms 内存限制:64 MB
A game of numbers has the following rules: at the beginning, two distinct positive integers are given by the judge. Then each player in turn must give a number to the judge. The number must be the difference of two numbers that are previously given, and must not be duplicated to any of the existed numbers. The game will run for several rounds. The one who gives a duplicate number or even a wrong number will be kicked out.
Your job is to write a judger program to judge the players' numbers and to determine the final winners.
Input Specification:
Each input file contains one test case. For each case, the first line gives two distinct positive integers to begin with. Both numbers are in [1,105].
In the second line, two numbers are given: N (2≤N≤10), the number of players, and M (2≤M≤103), the number of rounds.
Then N lines follow, each contains M positive integers. The i-th line corresponds to the i-th player (i=1,⋯,N). The game is to start from the 1st player giving his/her 1st number, followed by everybody else giving their 1st numbers in the 1st round; then everyone give their 2nd numbers in the 2nd round, and so on so forth.
Output Specification:
If the i
-th player is kicked out in the k
-th round, print in a line Round #k: i is out.
. The rest of the numbers given by the one who is out of the game will be ignored. If more than one player is out in the same round, print them in increasing order of their indices. When the game is over, print in the last line Winner(s): W1 W2 ... Wn
, where W1 ... Wn
are the indices of the winners in increasing order. All the numbers in a line must be separated by exactly one space, and there must be no extra space at the beginning or the end of the line. If there is no winner, print No winner.
instead.
Sample Input 1:
101 42
4 5
59 34 67 9 7
17 9 8 50 7
25 92 43 26 37
76 51 1 41 40
Sample Output 1:
Round #4: 1 is out.
Round #5: 3 is out.
Winner(s): 2 4
Sample Input 2:
42 101
4 5
59 34 67 9 7
17 9 18 50 49
25 92 58 1 39
102 32 2 6 41
Sample Output 2:
Round #1: 4 is out.
Round #3: 2 is out.
Round #4: 1 is out.
Round #5: 3 is out.
No winner.
解析:
题目大意:N个人依次报数,报的数必须是之前报过的某两个数之差取绝对值且不能重复,不然就出局,出局者报的这个数会被忽略掉。各个未出局者按顺序各自报完一遍视为一个回合,如此进行M个回合,问每回合哪些人出局,哪些人是最后的胜者。一开始会给定两个数作为初始值,视为报过的数。
例如样例1:一开始给了初始值{42,101},4个人,游戏进行五回合。第一回合,1号报了59,合法,报过的数为{42,59,101},2号报了17=59-42,合法,报过的数为{17,42,59,101},3号报了25=42-17,合法……第三回合结束后,报过的数为{1,8,9,17,25,34,42,43,51,59,67,76,92,101}。第四回合,1号报了9,重复,出局。第五回合,1号(已出局)报的数被无视,3号报之前,报过的数为{1,7,8,9,17,25,26,34,41,42,43,50,51,59,67,76,92,101},任意两数相减也得不到3号报的37,于是3号出局,这个数不加入集合。最后2和4号获胜。
一开始我用二分查找做的,测试点5老是超时。网上搜了下做法,好,我是傻逼。
改进:哈希存储所有合法的数,无论是否被报出过。每次有人报了新数,求出所有新产生的合法的数。再用另一个哈希表存放已经报过的数。
#include <vector>
#include <iostream>
using namespace std;
int N, M, input[1002][12], out[12]; // 参与人数,回合数,输入矩阵(行为回合,列为选手),选手是否出局
int hash1[100002], diff[100002]; // hash1记录所有报过的数,diff记录所有合法的数
vector<int> numbers; // 所有报过的数
void test() {
int t1, t2, i, j, winner_count;
scanf("%d %d", &t1, &t2);
// 用开始给的2个数对hash1、diff、numbers初始化
hash1[t1] = hash1[t2] = diff[abs(t1 - t2)] = 1;
numbers.push_back(t1); numbers.push_back(t2);
scanf("%d %d", &N, &M);
winner_count = N;
for (i = 1; i <= N; i++) {
for (j = 1; j <= M; j++) {
scanf("%d", &input[j][i]);
}
}
for (i = 1; i <= M; i++) {
vector<int> quit; // 本回合的出局者
for (j = 1; j <= N; j++) {
if (out[j]) continue; // 忽略出局人
// 如果报的数已经在hash1记录过,或者不是相减能得到的,出局
if (hash1[input[i][j]] == 1 || diff[input[i][j]] == 0) {
out[j] = 1; quit.push_back(j); winner_count--;
if (winner_count == 0) break;
continue;
}
// 新报的数,用已经报过的数逐一相减
for (auto item : numbers) diff[abs(input[i][j] - item)] = 1;
numbers.push_back(input[i][j]);
hash1[input[i][j]] = 1;
}
for (auto item : quit) {
printf("Round #%d: %d is out.\n", i, item);
}
if (winner_count == 0) break;
}
if (winner_count == 0) printf("No winner.");
else {
printf("Winner(s):");
for (i = 1; i <= N; i++) {
if (out[i] == 0) printf(" %d", i);
}
}
}
int main(){
test();
return 0;
}
7-3 Safari Park (25 分)
时间限制:300 ms 内存限制:64 MB
A safari park(野生动物园)has K species of animals, and is divided into N regions. The managers hope to spread the animals to all the regions, but not the same animals in the two neighboring regions. Of course, they also realize that this is an NP complete problem, you are not expected to solve it. Instead, they have designed several distribution plans. Your job is to write a program to help them tell if a plan is feasible.
Input Specification:
Each input file contains one test case. For each case, the first line gives 3 integers: N (0<N≤500), the number of regions; R (≥0), the number of neighboring relations, and K (0<K≤N), the number of species of animals. The regions and the species are both indexed from 1 to N.
Then R lines follow, each gives the indices of a pair of neighboring regions, separated by a space.
Finally there is a positive M (≤20) followed by M lines of distribution plans. Each plan gives N indices of species in a line (the i-th index is the animal in the i-th rigion), separated by spaces. It is guaranteed that any pair of neighboring regions must be different, and there is no duplicated neighboring relations.
Output Specification:
For each plan, print in a line Yes
if no animals in the two neighboring regions are the same, or No
otherwise. However, if the number of species given in a plan is not K, you must print Error: Too many species.
or Error: Too few species.
according to the case.
Sample Input:
6 8 3
2 1
1 3
4 6
2 5
2 4
5 4
5 6
3 6
5
1 2 3 3 1 2
1 2 3 4 5 6
4 5 6 6 4 5
2 3 4 2 3 4
2 2 2 2 2 2
Sample Output:
Yes
Error: Too many species.
Yes
No
Error: Too few species.
解析:
不用建图,把每对相邻区域保存下来逐个对比即可。
#include <vector>
#include <unordered_set>
#include <iostream>
using namespace std;
const int maxn = 502;
int N, R, K;
int plan[maxn];
vector<int> lefts, rights; // left[i]和right[i]合起来就是两个相邻区域,也可以用结构体或pair
bool judge () {
for (int i = 0; i < R; i++) {
if (plan[lefts[i]] == plan[rights[i]]) return false;
}
return true;
}
void test() {
int M, i, j, t1, t2;
scanf("%d %d %d", &N, &R, &K);
for (i = 0; i < R; i++) {
scanf("%d %d", &t1, &t2);
lefts.push_back(t1), rights.push_back(t2);
}
scanf("%d", &M);
for (i = 0; i < M; i++) {
unordered_set<int> set;
for (j = 1; j <= N; j++) {
scanf("%d", &plan[j]);
set.insert(plan[j]);
}
if (set.size() > K) printf("Error: Too many species.\n");
else if (set.size() < K) printf("Error: Too few species.\n");
else {
printf("%s\n", judge() ? "Yes" : "No");
}
}
}
int main(){
test();
return 0;
}
7-4 Replacement Selection (30 分)
时间限制:300 ms 内存限制:64 MB
When the input is much too large to fit into memory, we have to do external sorting instead of internal sorting. One of the key steps in external sorting is to generate sets of sorted records (also called runs) with limited internal memory. The simplest method is to read as many records as possible into the memory, and sort them internally, then write the resulting run back to some tape. The size of each run is the same as the capacity of the internal memory.
Replacement Selection sorting algorithm was described in 1965 by Donald Knuth. Notice that as soon as the first record is written to an output tape, the memory it used becomes available for another record. Assume that we are sorting in ascending order, if the next record is not smaller than the record we have just output, then it can be included in the run.
For example, suppose that we have a set of input { 81, 94, 11, 96, 12, 99, 35 }, and our memory can sort 3 records only. By the simplest method we will obtain three runs: { 11, 81, 94 }, { 12, 96, 99 } and { 35 }. According to the replacement selection algorithm, we would read and sort the first 3 records { 81, 94, 11 } and output 11 as the smallest one. Then one space is available so 96 is read in and will join the first run since it is larger than 11. Now we have { 81, 94, 96 }. After 81 is out, 12 comes in but it must belong to the next run since it is smaller than 81. Hence we have { 94, 96, 12 } where 12 will stay since it belongs to the next run. When 94 is out and 99 is in, since 99 is larger than 94, it must belong to the first run. Eventually we will obtain two runs: the first one contains { 11, 81, 94, 96, 99 } and the second one contains { 12, 35 }.
Your job is to implement this replacement selection algorithm.
Input Specification:
Each input file contains several test cases. The first line gives two positive integers N (≤105) and M (<N/2), which are the total number of records to be sorted, and the capacity of the internal memory. Then N numbers are given in the next line, all in the range of int. All the numbers in a line are separated by a space.
Output Specification:
For each test case, print in each line a run (in ascending order) generated by the replacement selection algorithm. All the numbers in a line must be separated by exactly 1 space, and there must be no extra space at the beginning or the end of the line.
Sample Input:
13 3
81 94 11 96 12 99 17 35 28 58 41 75 15
Sample Output:
11 81 94 96 99
12 17 28 35 41 58 75
15
解析:
大模拟题,要求模拟外部排序中的置换-选择排序,同时外部排序也是408考点之一。当外存中待排序的数太多,以至于超过了内存大小时,就要用到外部排序,即将这些数据分成多个有序归并段,再将这些归并段进行合并,生成更长的有序归并段,直到所有数据有序。为了生成初始归并段,当然可以按内存大小进行划分,例如题目中给的{81, 94, 11, 96, 12, 99, 35},内存只能容纳3个数,那么可以3个一组,分成{11, 81, 94}, {12, 96, 99}, {35}。而置换-选择排序可以产生更少更长的初始归并段,有利于降低外存的IO次数。
按照题目所给例子,置换-选择排序流程为:先把前3个数读入内存排好序,把11保存在外存中。再把96读入内存,此时内存中3个数为{81, 94, 96},将81保存在外存中作为初始归并段的开头。再把12读入内存,此时内存中为{12, 94, 96},由于外存中初始归并段为{11, 81},必须在内存中找出一个>81的数,选94写入外存,而12将会长期滞留在内存中。以此类推下去,在读入35后,内存中为{12, 35, 99},外存为{11, 81, 94, 96},选99写入外存。此时已经没有数据需要读入了,但是内存剩下的数仍要继续参与排序。而12和35都比99小,当前初始归并段再也不能增大,于是开一个新的初始归并段。最后我们得到了两个初始归并段{11, 81, 94, 96, 99}以及{12, 35}。
由此可以知道置换-选择排序的大致流程:先把足够的数据读入内存排好序,之后输出一个读入一个。每次输出的数必须不小于上一次输出的数,这意味着一些较小的数将不得不长期滞留在内存中。当内存中所有的数都比上一次输出的数小时,表明当前初始归并段已经不能再增长,那么开一个新的初始归并段,把内存中最小的数作为它的开头,这样迭代下去,直到内存变空为止。
故可以采用以下思路:
1、内存中需要对M个数排序,并且移除最小的数,用低优先级先出的优先队列最合适。
2、但是内存中有些数会长期滞留,而优先队列只能让最小的数出队,不能让次小或更大的数出队。为此使用一个remain数组,滞留的数全部放在这里。
3、刚开始先把优先队列填满。然后在循环中每次输出一个数,再读入一个数。读入的数如果比上次输出的数小,表明会长期滞留,就进remain,否则进优先队列。
4、如果要输出的时候优先队列为空,表示内存中全部是滞留的数,结束当前初始归并段,把remain的数全部加入优先队列,产生新的归并段。
5、可以把所有归并段记录下来统一输出,也可以在每个归并段结束的时候就输出当前归并段。如果采用后者,退出循环后,记得输出这一组数据。
#include <vector>
#include <queue>
#include <iostream>
using namespace std;
int N, M;
vector<int> input, ans, remain; // 待排序数列,归并段,滞留在内存的数
priority_queue<int, vector<int>, greater<>> pq; // 优先队列
void test() {
int i, j, max1 = 0, count = 0;
scanf("%d %d", &N, &M);
input.resize(N);
for (i = 0; i < N; i++) {
scanf("%d", &input[i]);
}
i = 0;
// 先把优先队列(内存)填满
while (pq.size() < M) pq.push(input[i++]);
// max1记录可以取出的值的下限。每次循环,先取出合适的值,再读入一个待排序值。
// 为了保证优先队列顶端一定是>=max1的值,读入的待排序值要判断一下,如果比max1小,就放入remain,表示会长期滞留内存中
while (count < N) {
// 当优先队列是为空,意味着所有数都比max1小了,输出当前归并段,并把remain所有值移到优先队列
if (pq.empty()) {
for (auto item : remain) {
pq.push(item);
}
for (j = 0; j < ans.size(); j++) {
printf("%d", ans[j]);
if (j < ans.size() - 1) printf(" ");
}
printf("\n");
remain.clear();
ans.clear();
}
max1 = pq.top();
ans.push_back(pq.top()); pq.pop();
count++;
if (i < N) {
if (input[i] < max1) {
remain.push_back(input[i]);
} else {
pq.push(input[i]);
}
i++;
}
}
for (j = 0; j < ans.size(); j++) {
printf("%d", ans[j]);
if (j < ans.size() - 1) printf(" ");
}
}
int main(){
test();
return 0;
}
总结
编号 | 标题 | 分数 | 类型 |
---|---|---|---|
7-1 | Prime Day | 20 | 5.4 素数 |
7-2 | The Judger | 25 | 4.2 哈希 |
7-3 | Safari Park | 25 | 3.1 简单模拟 |
7-4 | Replacement Selection | 30 | 13.3 快乐模拟 |
第一、三题很简单,第二题把所有可能性列出来的做法可能不太容易想到,反正我用二分做失败了。如果你能用二分AC,请务必让我看看代码。第四题如果不知道置换-选择排序,或者没看懂题目的话可能会卡很久,否则应该不难。总地来说,虽然有一些坑点,但考虑到秋和冬的难度,这次应该是当年最简单的。