PAT(甲級)2021年春季考試


7-1 Arithmetic Progression of Primes (20 分)

時間限制:1000 ms 內存限制:64 MB

In mathematics, an arithmetic progression (AP,等差數列) is a sequence of numbers such that the difference between the consecutive terms is constant. In 2004, Terence Tao (陶哲軒) and Ben Green proved that for any positive n, there exists at least one arithmetic progression consists of n consecutive prime numbers. For example, { 7,37,67,97,127,157 } is one solution for n=6. Now it is your job to find a maximum solution for a given n within a given range.

Input Specification:

Each input file contains one test case, which gives two positive integers in a line: n (≤10), the number of consecutive prime terms in an arithmetic progression, and MAXP (2≤MAXP<105), the upper bound of the largest prime in the solution.

Output Specification:

For each test case, if there exists a solution, print in ascending order the prime numbers in a line. If the solution is not unique, output the one with the maximum common difference. If there is still a tie, print the one with the maximum first number. If there is no solution bounded by MAXP, output the largest prime in the given range instead.

All the numbers in a line must be separated by a space, and there must be no extra space at the beginning or the end of the line.

Sample Input 1:

5 1000

Sample Output 1:

23 263 503 743 983

Sample Input 2:

10 200

Sample Output 2:

199

解析:

看到這1秒的限時就知道這題不簡單……

題目大意:找出一個全為質數的等差數列(公差為正),使得項數為給定的n,且末項不超過給定的MAXP。如果有多個解,輸出公差最大(第一判斷條件)且首項最大(第二判斷條件)的。如果無解,輸出小於等於MAXP的最大質數。

1、由於MAXP<105,可以直接用埃氏篩法打表;

2、既然求公差最大且首項最大的解,那么末項一定最大。進行搜索的時候,用兩層循環,外循環從大到小遍歷公差,內循環從大到小遍歷末項,找到的第一個滿足條件的等差數列就是解。先找到小於等於MAXP的最大質數last作為最大末項,再求公差的搜索范圍:最大公差為(last - 2) / (n - 1),例如樣例1給出n=5, MAXP=1000,求出last為997,那就是把[2, 997]的范圍分成4份並去尾,得最大公差為248。顯然,最小公差為1。有了公差,末項的搜索范圍就得到了:(last - 當前公差, last]。這樣得到的第一個滿足條件的解即為答案。

3、特判n為1的情形。

#include <iostream>
using namespace std;

const int maxn = 100002;

int isPrime[maxn], N;  // isPrime中0表示為質數,1表示不為質數

// 埃氏篩法
void getPrimes () {
    int i, j;
    for (i = 2; i < maxn; i++) {
        if (isPrime[i] == 0) {
            for (j = i + i; j < maxn; j += i) isPrime[j] = 1;
        }
    }
}

// 檢查對應末項和公差的數列是否滿足要求
bool check (int last, int diff) {
    int number = last;
    for (int i = 0; i < N; i++) {
        if (number < 2 || isPrime[number] == 1) return false;
        number -= diff;
    }
    return true;
}

void test () {
    int MAXP, i, j, k;
    scanf("%d %d", &N, &MAXP);
    getPrimes();
    isPrime[0] = isPrime[1] = 1;
    // 找到最大質數
    for (i = MAXP; i >= 2; i--) if (isPrime[i] == 0) break;
    if (N == 1) {
        printf("%d", i);
        return;
    }
    int last = i;  // 存最大質數
    int diff = (last - 2) / (N - 1);  // 最大公差
    for (j = diff; j >= 1; j--) {  // 公差
        for (i = last; i > last - j; i--) {  // 末項
            if (check(i, j)) {
                // 輸出解
                for (k = 0; k < N; k++) {
                    printf("%d", i - j * (N - k - 1));
                    if (k < N - 1) printf(" ");
                }
                return;
            }
        }
    }
    printf("%d", last);
}

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

7-2 Lab Access Scheduling (25 分)

時間限制:200 ms 內存限制:64 MB

Nowadays, we have to keep a safe social distance to stop the spread of virus due to the COVID-19 outbreak. Consequently, the access to a national lab is highly restricted. Everyone has to submit a request for lab use in advance and is only allowed to enter after the request has been approved. Now given all the personal requests for the next day, you are supposed to make a feasible plan with the maximum possible number of requests approved. It is required that at most one person can stay in the lab at any particular time.

Input Specification:

Each input file contains one test case. Each case starts with a positive integer N (≤2×103), the number of lab access requests. Then N lines follow, each gives a request in the format:

hh:mm:ss hh:mm:ss

where hh:mm:ss represents the time point in a day by hour:minute:second, with the earliest time being 00:00:00 and the latest 23:59:59. For each request, the two time points are the requested entrance and exit time, respectively. It is guaranteed that the exit time is after the entrance time.

Note that all times will be within a single day. Times are recorded using a 24-hour clock.

Output Specification:

The output is supposed to give the total number of requests approved in your plan.

Sample Input:

7
18:00:01 23:07:01
04:09:59 11:30:08
11:35:50 13:00:00
23:45:00 23:55:50
13:00:00 17:11:22
06:30:50 11:42:01
17:30:00 23:50:00

Sample Output:

5

Hint:

All the requests can be approved except the last two.

解析:

區間貪心。將各組時間轉成秒,按離開時間從小到大排序,第一個請求(最早離開的)予以批准,之后每一個請求,如果與上一次批准的區間有重疊就不予批准,直到出現一個不重疊的,批准,以此類推。

#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

int time2int (string& s) {
    int ans1 = 0;
    ans1 = (stoi(s.substr(0, 2)) * 3600 + stoi(s.substr(3, 2)) * 60 + stoi(s.substr(6, 2)));
    return ans1;
}

typedef struct Node {
    int a, b;
    Node (int a, int b) : a(a), b(b) {}
} Node;

bool cmp (Node a, Node b) {
    return a.b < b.b;
}

vector<Node> nodes;

void test () {
    int n, i, j;
    string s1, s2;

    scanf("%d", &n);
    for (i = 0; i < n; i++) {
        cin >> s1 >> s2;
        nodes.push_back(Node(time2int(s1), time2int(s2)));
    }
    sort(nodes.begin(), nodes.end(), cmp);
    int count = 1, last = 0;
    for (i = 1; i < n; i++) {
        if (nodes[i].a >= nodes[last].b) {
            count++;
            last = i;
        }
    }
    printf("%d", count);
}

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

7-3 Structure of Max-Heap (25 分)

時間限制:400 ms 內存限制:64 MB

In computer science, a max-heap is a specialized tree-based data structure that satisfies the heap property: if P is a parent node of C, then the key (the value) of P is greater than or equal to the key of C. A common implementation of a heap is the binary heap, in which the tree is a complete binary tree.

Your job is to first insert a given sequence of integers into an initially empty max-heap, then to judge if a given description of the resulting heap structure is correct or not. There are 5 different kinds of description statements:

  • x is the root
  • x and y are siblings
  • x is the parent of y
  • x is the left child of y
  • x is the right child of y

Input Specification:

Each input file contains one test case. For each case, the first line gives 2 positive integers: N (≤1,000), the number of keys to be inserted, and M (≤20), the number of statements to be judged. Then the next line contains N distinct integer keys in [−104,104] which are supposed to be inserted into an initially empty max-heap. Finally there are M lines of statements, each occupies a line.

Output Specification:

For each statement, print 1 if it is true, or 0 if not. All the answers must be print in one line, without any space.

Sample Input:

5 6
23 46 26 35 88
35 is the root
46 and 26 are siblings
88 is the parent of 46
35 is the left child of 26
35 is the right child of 46
-1 is the root

Sample Output:

011010

解析:

從空樹開始一個一個添加結點,每添加一個就將整棵樹調整至大根堆(而不能對整個序列進行建堆的操作)。然后用sscanf判斷是哪種情形。由於大根堆本來就是完全二叉樹,因此根、父親、左右孩子的下標都很容易求,剩下的就是體力活了。

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

using namespace std;

int n, m;
vector<int> tree;

// 調整
void adjust (int index) {
    int i = index / 2;
    while (i >= 1 && tree[i] < tree[index]) {
        swap(tree[i], tree[index]);
        index = i; i /= 2;
    }
}

void test () {
    int i;
    scanf("%d %d", &n, &m);
    tree.resize(n + 1);
    for (i = 1; i <= n; i++) {
        scanf("%d", &tree[i]);
        adjust(i);
    }
    unordered_map<int, int> map;  // 建立從結點數值到下標的映射
    for (i = 1; i <= n; i++) {
        map[tree[i]] = i;
    }
    string s;
    getchar();
    for (i = 0; i < m; i++) {
        getline(cin, s);
        int t1, t2;
        if (s.back() == 't') {
            t1 = stoi(s.substr(0, s.find('i') - 1));
            if (t1 == tree[1]) printf("1"); else printf("0");
        } else if (s.back() == 's') {
            sscanf(s.c_str(), "%d and %d are siblings", &t1, &t2);
            if (map[t1] / 2 == map[t2] / 2 ) printf("1"); else printf("0");
        } else if (s.find("parent") < s.length()) {
            sscanf(s.c_str(), "%d is the parent of %d", &t1, &t2);
            if (map[t1] == map[t2] / 2) printf("1"); else printf("0");
        } else if (s.find("left") < s.length()) {
            sscanf(s.c_str(), "%d is the left child of %d", &t1, &t2);
            if (map[t2] * 2 == map[t1]) printf("1"); else printf("0");
        } else {
            sscanf(s.c_str(), "%d is the right child of %d", &t1, &t2);
            if (map[t2] * 2 + 1 == map[t1]) printf("1"); else printf("0");
        }
    }
}

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

7-4 Recycling of Shared Bicycles (30 分)

時間限制:200 ms 內存限制:64 MB

There are many spots for parking the shared bicycles in Hangzhou. When some of the bicycles are broken, the management center will receive a message for sending a truck to collect them. Now given the map of city, you are supposed to program the collecting route for the truck. The strategy is a simple greedy method: the truck will always move to the nearest spot to collect the broken bicycles. If there are more than one nearest spot, take the one with the smallest index.

Input Specification:

Each input file contains one test case. For each case, the first line contains two positive integers: N (≤ 200), the number of spots (hence the spots are numbered from 1 to N, and the management center is always numbered 0), and M, the number of streets connecting those spots. Then M lines follow, describing the streets in the format:

S1 S2 Dist

where S1 and S2 are the spots at the two ends of a street, and Dist is the distance between them, which is a positive integer no more than 1000. It is guaranteed that each street is given once and S1 is never the same as S2.

Output Specification:

For each case, first print in a line the sequence of spots in the visiting order, starting from 0. If it is impossible to collect all the broken bicycles, output in the second line those spots that cannot be visited, in ascending order of their indices. Or if the job can be done perfectly, print in the second line the total moving distance of the truck.

All the numbers in a line must be separated by 1 space, and there must be no extra space at the beginning or the end of the line.

Sample Input 1 (shown by the figure below):

7 10
0 2 1
0 4 5
0 7 3
0 6 4
0 5 5
1 2 2
1 7 2
2 3 4
3 4 2
6 7 9

sample.JPG

Sample Output 1:

0 2 1 7 6 3 4 5
33

Sample Input 2:

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

Sample Output 2:

0 2 1 7 3 4
5 6

解析:

題目大意:給定一張無向圖和一個起點0,從起點開始,每次前往下一個最近的且沒有訪問過的點(允許中途路過曾經訪問的點),求這些距離之和。如果按照這樣的策略無法到達每個點,就把那些到不了的點打印出來。

樣例1:從0開始,最近的點為2。從2開始,最近的未訪問點為1。……這樣順序為0→2→1→7→6,從6開始,最近的未訪問點有3、4、5,前往最小的3。以此類推,最后總路程為35。

樣例2:

如圖所示,顯然6和5到不了。

方法一:多次使用迪傑斯特拉算法。可以用一個set維護所有還未訪問的點。

#include <vector>
#include <algorithm>
#include <set>
#include <iostream>

using namespace std;

const int INF = 0x3fffffff, maxn = 202;

typedef struct Node {
    int to, dis;
    Node (int t, int d) : to(t), dis(d) {}
} Node;

int N, M;
int dist[maxn], visited[maxn];
vector<Node> G[maxn];

void dij (int start) {
    int i, j;
    fill(visited, visited + maxn, 0);
    fill(dist, dist + maxn, INF);
    dist[start] = 0;
    for (i = 0; i <= N; i++) {
        int cur = -1, min_dist = INF;
        for (j = 0; j <= N; j++)
            if (!visited[j] && dist[j] < min_dist) cur = j, min_dist = dist[j];
        if (cur == -1) return;
        visited[cur] = 1;
        for (auto& item : G[cur]) {
            if (visited[item.to]) continue;
            if (dist[cur] + item.dis < dist[item.to]) {
                dist[item.to] = dist[cur] + item.dis;
            }
        }
    }
}

void test () {
    int i, t1, t2, t3;
    scanf("%d %d", &N, &M);
    for (i = 0; i < M; i++) {
        scanf("%d %d %d", &t1, &t2, &t3);
        G[t1].push_back(Node(t2, t3));
        G[t2].push_back(Node(t1, t3));
    }
    set<int> to_visit;  // 所有待訪問的點
    for (i = 1; i <= N; i++) to_visit.insert(i);
    int sum = 0, start = 0;
    printf("0");
    while (!to_visit.empty()) {
        dij(start);
        int min_d = INF, index = -1;
        // 在未訪問點中找到最近的點
        for (auto item : to_visit) {
            if (dist[item] < min_d) min_d = dist[item], index = item;
        }
        if (index == -1) break;
        printf(" %d", index);
        sum += min_d;
        to_visit.erase(index);
        start = index;
    }
    printf("\n");
    if (to_visit.empty()) {
        printf("%d", sum);
    } else {
        i = 0;
        for (auto item : to_visit) {
            printf("%d", item);
            if (i < to_visit.size() - 1) printf(" ");
            i++;
        }
    }
}

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

方法二:要求所有點之間的最短距離,用弗洛伊德算法。

#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

const int INF = 0x3fffffff, maxn = 202;

int N, M, G[maxn][maxn];
int visited[maxn];

void floyd () {
    for (int k = 0; k <= N; k++) {
        for (int i = 0; i <= N; i++) {
            for (int j = 0; j <= N; j++) {
                G[i][j] = min(G[i][j], G[i][k] + G[k][j]);
            }
        }
    }
}

void test () {
    int i, j, t1, t2, t3;
    scanf("%d %d", &N, &M);
    fill(G[0], G[0] + maxn * maxn, INF);
    for (i = 0; i < M; i++) {
        scanf("%d %d %d", &t1, &t2, &t3);
        G[t1][t2] = G[t2][t1] = t3;
    }
    floyd();
    printf("0");
    int sum = 0, start = 0;
    visited[0] = 1;
    for (i = 0; i < N; i++) {
        // 每次前往下一個最近的且沒有訪問過的點
        int min_index = -1, min_dist = INF;
        for (j = 0; j <= N; j++) {
            if (visited[j] == 0 && G[start][j] < min_dist) {
                min_dist = G[start][j];
                min_index = j;
            }
        }
        if (min_index == -1) {
            break;
        }
        printf(" %d", min_index);
        sum += min_dist;
        visited[min_index] = 1;
        start = min_index;
    }
    printf("\n");
    if (i == N) printf("%d", sum);
    else {
        for (i = 0, j = 0; j <= N; j++) {
            if (visited[j] == 0) {
                if (i > 0) printf(" ");
                printf("%d", j);
                i++;
            }
        }
    }
}

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

總結

編號 標題 分數 類型
7-1 Arithmetic Progression of Primes 20 5.4 素數
7-2 Lab Access Scheduling 25 4.4 貪心
7-3 Structure of Max-Heap 25 9.7 堆
7-4 Recycling of Shared Bicycles 30 10.4 最短路徑

20和19年的甲級春在當年的三次考試中是比較簡單的,21年甲級春似乎要繼續沿襲這個風格了(?)。就考點而言,數學問題、算法思想、樹、圖都有涉及,考察得還是蠻廣泛的。就考察頻率而言,值得注意的是,如果我沒有記錯,區間貪心應該是甲級第一次考察。第四題也是,至少對我而言,看到最短路徑下意識就是迪傑斯特拉算法,但這題用弗洛伊德的的確確更簡潔一點。總地來說學習還是得全面,不能光做老題。也許下一次負權圖或者關鍵路徑就出現了呢。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM