PAT(甲级)2019年春季考试

7-1 Sexy Primes (20 分)

时间限制:400 ms 内存限制:64 MB

Sexy primes are pairs of primes of the form (p, p+6), so-named since "sex" is the Latin word for "six". (Quoted from http://mathworld.wolfram.com/SexyPrimes.html)

Now given an integer, you are supposed to tell if it is a sexy prime.

Input Specification:

Each input file contains one test case. Each case gives a positive integer N (≤10​8).

Output Specification:

For each case, print in a line Yes if N is a sexy prime, then print in the next line the other sexy prime paired with N (if the answer is not unique, output the smaller number). Or if N is not a sexy prime, print No instead, then print in the next line the smallest sexy prime which is larger than N.

Sample Input 1:

47

Sample Output 1:

Yes
41

Sample Input 2:

21

Sample Output 2:

No
23

解析:

1、如果给出的num和num-6都是质数,则输出Yes和num-6。否则,如果num和num+6都是质数,则输出Yes和num+6。如果上述条件都不满足,则让num自增,直到num和num-6都是质数,或num和num+6都是质数为止,输出No和num。

2、注意,小于等于1的都不是质数。

#include <cmath>
#include <iostream>

using namespace std;

bool isPrime (int number) {
    if (number <= 1) return false;
    int sqr = sqrt(number);
    for (int i = 2; i <= sqr; i++) {
        if (number % i == 0) return false;
    }
    return true;
}

void test () {
    int n, i;
    scanf("%d", &n);
    if (isPrime(n)) {
        if (isPrime(n - 6)) {
            printf("Yes\n%d", n - 6);
            return;
        } 
        if (isPrime(n + 6)) {
            printf("Yes\n%d", n + 6);
            return;
        }
    }
    for (i = n + 1; ; i++) {
        if (isPrime(i) && (isPrime(i - 6) || isPrime(i + 6))) {
            printf("No\n%d", i);
            return;
        }
    }
}

int main () {
    test ();
    return 0;
}

7-2 Anniversary (25 分)

时间限制:800 ms 内存限制:64 MB

Zhejiang University is about to celebrate her 122th anniversary in 2019. To prepare for the celebration, the alumni association (校友会) has gathered the ID's of all her alumni. Now your job is to write a program to count the number of alumni among all the people who come to the celebration.

Input Specification:

Each input file contains one test case. For each case, the first part is about the information of all the alumni. Given in the first line is a positive integer N (≤105). Then N lines follow, each contains an ID number of an alumnus. An ID number is a string of 18 digits or the letter X. It is guaranteed that all the ID's are distinct.

The next part gives the information of all the people who come to the celebration. Again given in the first line is a positive integer M (≤105). Then M lines follow, each contains an ID number of a guest. It is guaranteed that all the ID's are distinct.

Output Specification:

First print in a line the number of alumni among all the people who come to the celebration. Then in the second line, print the ID of the oldest alumnus -- notice that the 7th - 14th digits of the ID gives one's birth date. If no alumnus comes, output the ID of the oldest guest instead. It is guaranteed that such an alumnus or guest is unique.

Sample Input:

5
372928196906118710
610481197806202213
440684198612150417
13072819571002001X
150702193604190912
6
530125197901260019
150702193604190912
220221196701020034
610481197806202213
440684198612150417
370205198709275042

Sample Output:

3
150702193604190912

解析:

1、用set把校友存起来方便查找。

2、在输入来宾名单时,一边求来宾中最年长的人,一边求到访校友中最年长的人,根据有无校友到来输出对应ID。

#include <unordered_set>
#include <iostream>

using namespace std;

int N, M;
unordered_set<string> set;

void test () {
    // min_year1是来宾中最早的年份,min_year2是到访校友中最年长的人
    int i, year, min_year1 = 9999, min_year2 = 9999;
    int count = 0;  // 到来校友数
    // ans1是来宾中最年长的人,ans2是到访校友中最年长的人
    string s, ans1, ans2;
    scanf("%d", &N);
    for (i = 0; i < N; i++) {
        cin >> s;
        set.insert(s);
    }
    scanf("%d", &M);
    for (i = 0; i < M; i++) {
        cin >> s;
        year = stoi(s.substr(6, 4));
        if (year < min_year2) {
            ans2 = s; min_year2 = year;
        }
        if (set.count(s)) {
            count++;
            if (year < min_year1) {
                ans1 = s; min_year1 = year;
            }
        }
    }
    printf("%d\n%s", count, count ? ans1.c_str() : ans2.c_str());

}

int main () {
    test ();
    return 0;
}

7-3 Telefraud Detection (25 分)

时间限制:400 ms 内存限制:64 MB

Telefraud(电信诈骗) remains a common and persistent problem in our society. In some cases, unsuspecting victims lose their entire life savings. To stop this crime, you are supposed to write a program to detect those suspects from a huge amount of phone call records.

A person must be detected as a suspect if he/she makes more than K short phone calls to different people everyday, but no more than 20% of these people would call back. And more, if two suspects are calling each other, we say they might belong to the same gang. A makes a short phone call to B means that the total duration of the calls from A to B is no more than 5 minutes.

Input Specification:

Each input file contains one test case. For each case, the first line gives 3 positive integers K (≤500, the threshold(阈值) of the amount of short phone calls), N (≤103, the number of different phone numbers), and M (≤105, the number of phone call records). Then M lines of one day's records are given, each in the format:

caller receiver duration

where caller and receiver are numbered from 1 to N, and duration is no more than 1440 minutes in a day.

Output Specification:

Print in each line all the detected suspects in a gang, in ascending order of their numbers. The gangs are printed in ascending order of their first members. 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.

If no one is detected, output None instead.

Sample Input 1:

5 15 31
1 4 2
1 5 2
1 5 4
1 7 5
1 8 3
1 9 1
1 6 5
1 15 2
1 15 5
3 2 2
3 5 15
3 13 1
3 12 1
3 14 1
3 10 2
3 11 5
5 2 1
5 3 10
5 1 1
5 7 2
5 6 1
5 13 4
5 15 1
11 10 5
12 14 1
6 1 1
6 9 2
6 10 5
6 11 2
6 12 1
6 13 1

Sample Output 1:

3 5
6

Note: In sample 1, although 1 had 9 records, but there were 7 distinct receivers, among which 5 and 15 both had conversations lasted more than 5 minutes in total. Hence 1 had made 5 short phone calls and didn't exceed the threshold 5, and therefore is not a suspect.

Sample Input 2:

5 7 8
1 2 1
1 3 1
1 4 1
1 5 1
1 6 1
1 7 1
2 1 1
3 1 1

Sample Output 2:

None

解析:

1、仔细读题,看清判断条件:

short phone指给单人的通话总时长小于等于5分钟;

设count1是给不同人short phone的数量,count2是接到short phone中回拨的人数,满足count1 > K且count2 * 5 <= count1的人是可疑分子;

只要两个可疑分子互相通了电话(无论时长),这两个人就是一伙的。

2、建立邻接表,统计从A到B的总通话时长。为了方便查询B是否拨打A,再建一个二维hash表。

3、先找出所有可疑分子,再对这些人初始化并查集,最后两两判断是否通过电话。

#include <vector>
#include <unordered_map>
#include <map>
#include <algorithm>
#include <iostream>

using namespace std;

const int maxn = 1002;

int K, N, M;
unordered_map<int, int> G[maxn];  // 邻接表
int G2[maxn][maxn];  // 哈希表,G2[a][b] == 1表示a给b打过电话
unordered_map<int, int> father;  // 并查集
map<int, vector<int>> ans;  // 存储输出结果,key为团伙头目(最小序号者),value为所有同伙(包括自己)

int findFather (int index) {
    int x = index;
    while (x != father[x]) {
        x = father[x];
    }
    return x;
}

void Union (int a, int b) {
    int fa = findFather(a);
    int fb = findFather(b);
    if (fa == fb) return;
    // 保证序号小的成为头
    if (fa < fb) father[fb] = fa;
    else father[fa] = fb;
}

void test () {
    int i, t1, t2, t3;
    scanf("%d %d %d", &K, &N, &M);
    for (i = 0; i < M; i++) {
        scanf("%d %d %d", &t1, &t2, &t3);
        G[t1][t2] += t3;
        G2[t1][t2] = 1;
    }
    for (i = 1; i <= N; i++) {
        // count1是给不同人short phone的数量,count2是接到short phone中回拨的人数
        int count1 = 0, count2 = 0;
        // 简单优化:如果i的拨打人数小于等于阈值,i一定不是可疑分子
        if (G[i].size() <= K) continue;
        for (auto& item : G[i]) {
            if (item.second <= 5) {
                count1++;
                if (G2[item.first][i]) {
                    count2++;
                }
            }
        }
        // 挑出可疑分子,初始化并查集
        if (count1 > K && count2 * 5 <= count1) {
            father[i] = i;
        }
    }
    if (father.empty()) {
        printf("None");
        return;
    }
    // 双重循环,可疑分子两两合并
    for (auto it1 = father.begin(); it1 != father.end(); it1++) {
        auto it2 = it1;
        int a = it1->first;
        for (it2++; it2 != father.end(); it2++) {
            int b = it2->first;
            if (G2[a][b] && G2[b][a]) {
                Union(a, b);
            }
        }
    }
    // 整理各团伙成员及头目,头目作为key,让map自动排序
    for (auto &item : father) {
        ans[findFather(item.first)].push_back(item.first);
    }
    for (auto &item : ans) {
        vector<int> &temp = item.second;
        sort(temp.begin(), temp.end());
        for (i = 0; i < temp.size(); i++) {
            printf("%d%c", temp[i], i < temp.size() - 1 ? ' ' : '\n');
        }
    }

}

int main () {
    test ();
    return 0;
}

7-4 Structure of a Binary Tree (30 分)

时间限制:400 ms 内存限制:64 MB

Suppose that all the keys in a binary tree are distinct positive integers. Given the postorder and inorder traversal sequences, a binary tree can be uniquely determined.

Now given a sequence of statements about the structure of the resulting tree, you are supposed to tell if they are correct or not. A statment is one of the following:

  • A is the root
  • A and B are siblings
  • A is the parent of B
  • A is the left child of B
  • A is the right child of B
  • A and B are on the same level
  • It is a full tree

Note:

  • Two nodes are on the same level, means that they have the same depth.
  • A full binary tree is a tree in which every node other than the leaves has two children.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (≤30), the total number of nodes in the binary tree. The second line gives the postorder sequence and the third line gives the inorder sequence. All the numbers in a line are no more than 103 and are separated by a space.

Then another positive integer M (≤30) is given, followed by M lines of statements. It is guaranteed that both A and B in the statements are in the tree.

Output Specification:

For each statement, print in a line Yes if it is correct, or No if not.

Sample Input:

9
16 7 11 32 28 2 23 8 15
16 23 7 32 11 2 28 15 8
7
15 is the root
8 and 2 are siblings
32 is the parent of 11
23 is the left child of 16
28 is the right child of 2
7 and 11 are on the same level
It is a full tree

Sample Output:

Yes
No
Yes
No
Yes
Yes
Yes

解析:

脑力活不大,体力活倒是很多的题目……

1、根据中序和后序建树。

2、由于各结点值互不相同,为了方便查找各结点,建立结点值到结点指针的映射。

3、为了方便判断两个结点是否兄弟,在结点的结构体数组中新增一个指向父节点的指针(即三叉链表),建树的时候即可确定。

4、为了方便判断两个结点是否在同一层,在结点的结构体数组中新增层号,建树的时候即可确定。

5、判断是否完全二叉树的方法:当某个结点无左子树却有右子树,那么这棵树一定不是完全二叉树。

6、读入一行用getline,取得对应数据用sscanf。

#include <vector>
#include <unordered_map>
#include <iostream>

using namespace std;

typedef struct Node {
    int data, layer;
    struct Node *l = NULL, *r = NULL, *f = NULL;  // 左子树,右子树,父结点
} Node;

int N, M;
vector<int> in, post;
unordered_map<int, Node*> tree;  // 根据结点值找到结点
Node *root = NULL;
int flag = 0;  // 是否完全二叉树,0为是

Node *create (int in1, int in2, int post1, int post2, Node *father, int layer) {
    if (in1 > in2) return NULL;
    int i = in1;
    while (in[i] != post[post2]) i++;
    int len = i - in1;
    Node *node = new Node;
    node->data = post[post2];
    node->layer = layer;
    node->f = father;
    tree[post[post2]] = node;
    node->l = create(in1, i - 1, post1, post1 + len - 1, node, layer + 1);
    node->r = create(i + 1, in2, post1 + len, post2 - 1, node, layer + 1);
    if (node->l == NULL && node->r != NULL) flag = 1;
    return node;
}

void test () {
    int i, t1, t2;
    string s;
    scanf("%d", &N);
    post.resize(N); in.resize(N);
    for (i = 0; i < N; i++) scanf("%d", &post[i]);
    for (i = 0; i < N; i++) scanf("%d", &in[i]);
    root = create(0, N - 1, 0, N - 1, NULL, 0);
    scanf("%d\n", &M);
    for (i = 0; i < M; i++) {
        getline(cin, s);
        if (s.back() == 't') {
            sscanf(s.c_str(), "%d is the root", &t1);
            printf("%s\n", root->data == t1 ? "Yes" : "No");
        } else if (s.back() == 's') {
            sscanf(s.c_str(), "%d and %d are siblings", &t1, &t2);
            printf("%s\n", tree[t1]->f == tree[t2]->f ? "Yes" : "No");
        } else if (s.find("parent") < s.length()) {
            sscanf(s.c_str(), "%d is the parent of %d", &t1, &t2);
            printf("%s\n", tree[t2]->f == tree[t1] ? "Yes" : "No");
        } else if (s.find("left") < s.length()) {
            sscanf(s.c_str(), "%d is the left child of %d", &t1, &t2);
            printf("%s\n", tree[t2]->l == tree[t1] ? "Yes" : "No");
        } else if (s.find("right") < s.length()) {
            sscanf(s.c_str(), "%d is the right child of %d", &t1, &t2);
            printf("%s\n", tree[t2]->r == tree[t1] ? "Yes" : "No");
        } else if (s.back() == 'l') {
            sscanf(s.c_str(), "%d and %d are on the same level", &t1, &t2);
            printf("%s\n", tree[t1]->layer == tree[t2]->layer ? "Yes" : "No");
        } else {
            printf("%s\n", flag == 0 ? "Yes" : "No");
        }
    }
}

int main () {
    test ();
    return 0;
}

总结

编号 标题 分数 类型
7-1 Sexy Primes 20 5.4 素数
7-2 Anniversary 25 6.2 set的应用
7-3 Telefraud Detection 25 9.6 并查集
7-4 Structure of a Binary Tree 30 9.2 二叉树的遍历

按通过率来看,第三题最难。第一题在求素数的基础上加了点逻辑判断,如果没理清逻辑可能会被卡,但总体很简单。第二题相当于签到题。第三题难在,一方面题目长,关键的信息被隐藏在好几个地方;另一方面在图的存储之上还要用并查集,怎么维护数据结构也是个问题。第四题对树的概念考察得比较多,不知道有没有人死在sibling这个单词上(滑稽)。该题在用中序+另一种序列建树的基础上套了个壳,所以我们也要对模板做些改进。此外,如果想不到sscanf的话可能麻烦点,这是个平时用不到,关键时候很好用的东西。最后总结下本次考试:粗中有细,笑里藏刀(

posted @ 2021-04-05 00:15  雪上空留马行处  阅读(345)  评论(0编辑  收藏  举报