PAT(甲級)2020年秋季考試


7-1 Panda and PP Milk (20 分)

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

panda.jpg

PP milk (盆盆奶)is Pandas' favorite. They would line up to enjoy it as show in the picture. On the other hand, they could drink in peace only if they believe that the amount of PP milk is fairly distributed, that is, fatter panda can have more milk, and the ones with equal weight may have the same amount. Since they are lined up, each panda can only compare with its neighbor(s), and if it thinks this is unfair, the panda would fight with its neighbor.

Given that the minimum amount of milk a panda must drink is 200 ml. It is only when another bowl of milk is at least 100 ml more than its own that a panda can sense the difference.

Now given the weights of a line of pandas, your job is to help the breeder(飼養員)to decide the minimum total amount of milk that he/she must prepare, provided that the pandas are lined up in the given order.

Input Specification:

Each input file contains one test case. For each case, first a positive integer n (≤104) is given as the number of pandas. Then in the next line, n positive integers are given as the weights (in kg) of the pandas, each no more than 200. the numbers are separated by spaces.

Output Specification:

For each test case, print in a line the minimum total amount of milk that the breeder must prepare, to make sure that all the pandas can drink in peace.

Sample Input:

10
180 160 100 150 145 142 138 138 138 140

Sample Output:

3000

Hint:

The distribution of milk is the following:

400 300 200 500 400 300 200 200 200 300

解析:

“臭名昭著”的難題……

方法一:每個熊貓要喝的奶只與它左右兩側的熊貓有關。PAT題庫里有兩道類似的題:【B1045/A1101 Quick Sort】【B1040/A1093 Count PAT's】。前者要求判斷數組上每個值左邊所有數是否都比該值小,以及右邊所有數是否都比該值大。后者可以轉化為以字母A為參照,統計字符串中每個A字符左邊P字符的個數,以及右邊T字符的個數。

這道題也可以用類似的思路,掃兩遍數組,第一遍從左往右分配,當前熊貓體重大於左邊就+100,等於就分一樣的奶,小於左邊就只分配200,這樣每個熊貓不會和左邊的熊貓打架;第二遍從右往左分配,方法相同,這樣每個熊貓不會和右邊的熊貓打架。最后把兩個數組合並,合並方式是取較大值。

#include <vector>
#include <iostream>

using namespace std;

void test () {
    int n, i, sum = 0;
    scanf("%d", &n);
    vector<int> input(n), a(n), b(n);
    for (i = 0; i < n; i++) scanf("%d", &input[i]);
    a[0] = 200;
    for (i = 1; i < n; i++) {
        if (input[i] > input[i - 1]) a[i] = a[i - 1] + 100;
        else if (input[i] == input[i - 1]) a[i] = a[i - 1];
        else a[i] = 200;
    }
    b.back() = 200;
    for (i = n - 2; i >= 0; i--) {
        if (input[i] > input[i + 1]) b[i] = b[i + 1] + 100;
        else if (input[i] == input[i + 1]) b[i] = b[i + 1];
        else b[i] = 200;
        sum += max(a[i], b[i]);
    }
    sum += max(a.back(), b.back());
    printf("%d", sum);
}

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

方法二:如果體重從前往后不變/遞增,那么分配的奶會相同/變多。如果下降,要分配的奶不好確定,因為要受到右邊熊貓的影響,但是可以先分配200。 接着判斷左邊的熊貓是否滿意,如果不滿意,就給左邊的熊貓+100,接着繼續判斷左邊的左邊的熊貓是否滿意,直到某個左邊的熊貓滿意為止。

#include <vector>
#include <iostream>

using namespace std;

int main(){
    int n, sum = 0, i, j;
    scanf("%d", &n);
    vector<int> panda(n), milk(n);
    scanf("%d", &panda[0]);
    milk[0] = 200;
    for(i = 1;i < n; i++){
        scanf("%d", &panda[i]);
        if (panda[i] == panda[i - 1]) milk[i] = milk[i - 1];
        else if (panda[i] > panda[i - 1]) {
            milk[i] = milk[i - 1] + 100;
        } else {
            milk[i] = 200;
            for(j = i - 1; j >= 0; j--) {
                if (panda[j] >= panda[j + 1] && milk[j] <= milk[j + 1])
                    milk[j] += 100;
                else break;
            }

        }
    }
    for(i = 0; i < n; i++){
        sum += milk[i];
    }
    printf("%d", sum);
    return 0;
}

7-2 How Many Ways to Buy a Piece of Land (25 分)

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

The land is for sale in CyberCity, and is divided into several pieces. Here it is assumed that each piece of land has exactly two neighboring pieces, except the first and the last that have only one. One can buy several contiguous(連續的) pieces at a time. Now given the list of prices of the land pieces, your job is to tell a customer in how many different ways that he/she can buy with a certain amount of money.

Input Specification:

Each input file contains one test case. Each case first gives in a line two positive integers: N (≤104), the number of pieces of the land (hence the land pieces are numbered from 1 to N in order), and M (≤109), the amount of money that your customer has.

Then in the next line, N positive integers are given, where the i-th one is the price of the i-th piece of the land.

It is guaranteed that the total price of the land is no more than 109.

Output Specification:

For each test case, print the number of different ways that your customer can buy. Notice that the pieces must be contiguous.

Sample Input:

5 85
38 42 15 24 9

Sample Output:

11

Hint:

The 11 different ways are:

38
42
15
24
9
38 42
42 15
42 15 24
15 24
15 24 9
24 9

解析:

題目大意:有一組正整數,求連續的子序列,使子序列的和 ≤ 給定的數,問這樣的子序列有多少個。

方法一:DFS+剪枝,來得及。

#include <vector>
#include <iostream>
using namespace std;

int n, m;
vector<int> prices, ans;
int count = 0;

void DFS (int index, int sum) {
    if (index >= n || sum > m) return;
    if (sum + prices[index] <= m) {
        ans.push_back(prices[index]);
        count++;
        DFS(index + 1, sum + prices[index]);
    }
}

void test() {
    int i, j, temp;
    scanf("%d %d", &n, &m);
    for (i = 0; i < n; i++) {
        scanf("%d", &temp);
        prices.push_back(temp);
    }
    for (i = 0; i < n; i++) {
        ans.clear();
        DFS(i, 0);
    }
    printf("%d", count);
}

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

方法二:既然是連續的子序列的和,可以求前綴和,然后雙重循環暴力找。

優化:前綴和一定是有序的,內層循環可以用二分找到第一個大於sums[i]+M的數,而不是順序查找,就不貼代碼啦~(其實是懶得再花5塊錢驗證了……)

#include <vector>
#include <iostream>

using namespace std;

int N, M;
vector<int> sums;
int ans = 0;

void test () {
    int i, j, temp1, sum = 0;
    scanf("%d %d", &N, &M);
    sums.push_back(0);
    for (i = 0; i < N; i++) {
        scanf("%d", &temp1);
        sum += temp1;
        sums.push_back(sum);
    }
    for (i = 0; i < (int)sums.size() - 1; i++) {
        for (j = i + 1; j < sums.size(); j++) {
            if (sums[j] - sums[i] <= M) ans++;
        }
    }
    printf("%d", ans);
}

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

7-3 Left-View of Binary Tree (25 分)

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

The left-view of a binary tree is a list of nodes obtained by looking at the tree from left hand side and from top down. For example, given a tree shown by the figure, its left-view is { 1, 2, 3, 4, 5 }

fig.JPG

Given the inorder and preorder traversal sequences of a binary tree, you are supposed to output its left-view.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (≤20), which is the total number of nodes in the tree. Then given in the following 2 lines are the inorder and preorder traversal sequences of the tree, respectively. All the keys in the tree are distinct positive integers in the range of int.

Output Specification:

For each case, print in a line the left-view of the tree. All the numbers in a line are separated by exactly 1 space, and there must be no extra space at the beginning or the end of the line.

Sample Input:

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

Sample Output:

1 2 3 4 5

解析:

方法一:根據中序和后序建樹,然后層次遍歷,每層只記錄第一個點。

#include <vector>
#include <queue>
#include <iostream>

using namespace std;

typedef struct Node {
    int data, layer;
    struct Node *l = NULL, *r = NULL;
} Node;

Node *root = NULL;
int N;
vector<int> pre, in, ans;

Node *create (int pre1, int pre2, int in1, int in2) {
    if (pre1 > pre2) return NULL;
    int i = in1;
    while (in[i] != pre[pre1]) i++;
    int len = i - in1;
    Node *node = new Node;
    node->data = pre[pre1];
    node->l = create(pre1 + 1, pre1 + len, in1, i - 1);
    node->r = create(pre1 + len + 1, pre2, i + 1, in2);
    return node;
}

void BFS () {
    queue<Node*> q;
    q.push(root);
    root->layer = 1;
    ans.push_back(root->data);
    while (!q.empty()) {
        Node *node = q.front(); q.pop();
        // 每層只記錄第一個點
        if (node->l != NULL) {
            node->l->layer = node->layer + 1;
            q.push(node->l);
            if (ans.size() == node->layer) ans.push_back(node->l->data);
        }
        if (node->r != NULL) {
            node->r->layer = node->layer + 1;
            q.push(node->r);
            if (ans.size() == node->layer) ans.push_back(node->r->data);
        }
    }
}

void test () {
    int i;
    scanf("%d", &N);
    pre.resize(N); in.resize(N);
    for (i = 0; i < N; i++) scanf("%d", &in[i]);
    for (i = 0; i < N; i++) scanf("%d", &pre[i]);
    root = create(0, N - 1, 0, N - 1);
    BFS();
    for (i = 0; i < ans.size(); i++) {
        printf("%d", ans[i]);
        if (i < ans.size() - 1) printf(" ");
    }
}

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

方法二:不用建樹。

#include <vector>
#include <iostream>
using namespace std;

int n;
vector<int> pre, in, ans;
int max_layer = -1;

void create (int left1, int right1, int left2, int right2, int layer) {
    if (left1 > right1) return;
    int node = pre[left1], i = left2;
    while (in[i] != node) i++;
    // 因為是先處理左子樹再處理右子樹,該層處理的第一個結點一定符合要求
    if (ans[layer] == 0) ans[layer] = node;
    if (max_layer < layer) max_layer = layer;
    int L = i - left2;
    create(left1 + 1, left1 + L, left2, i - 1, layer + 1);
    create(left1 + 1 + L, right1, i + 1, right2, layer + 1);
}

void test() {
    int i;
    scanf("%d", &n);
    pre.resize(n); in.resize(n);
    ans.resize(n, 0);  // ans最長為n
    for (i = 0; i < n; i++) scanf("%d", &in[i]);
    for (i = 0; i < n; i++) scanf("%d", &pre[i]);
    create(0, n - 1, 0, n - 1, 0);
    for (i = 0; i <= max_layer; i++) {
        printf("%d", ans[i]);
        if (i < max_layer) printf(" ");
    }
}

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

7-4 Professional Ability Test (30 分)

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

Professional Ability Test (PAT) consists of several series of subject tests. Each test is divided into several levels. Level A is a prerequisite (前置要求) of Level B if one must pass Level A with a score no less than S in order to be qualified to take Level B. At the mean time, one who passes Level A with a score no less than S will receive a voucher(代金券)of D yuans (Chinese dollar) for taking Level B.

At the moment, this PAT is only in design and hence people would make up different plans. A plan is NOT consistent if there exists some test T so that T is a prerequisite of itself. Your job is to test each plan and tell if it is a consistent one, and at the mean time, find the easiest way (with minimum total S) to obtain the certificate of any subject test. If the easiest way is not unique, find the one that one can win the maximum total value of vouchers.

Input Specification:

Each input file contains one test case. For each case, the first line gives two positive integers N (≤1000) and M, being the total numbers of tests and prerequisite relations, respectively. Then M lines follow, each describes a prerequisite relation in the following format:

T1 T2 S D

where T1 and T2 are the indices (from 0 to N−1) of the two distinct tests; S is the minimum score (in the range (0, 100]) required to pass T1 in order to be qualified to take T2; and D is the value of the voucher (in the range (0, 500]) one can receive if one passes T1 with a score no less than S and plan to take T2. It is guaranteed that at most one pair of S and D are defined for a prerequisite relation.

Then another positive integer K (≤N) is given, followed by K queries of tests. All the numbers in a line are separated by spaces.

Output Specification:

Print in the first line Okay. if the whole plan is consistent, or Impossible. if not.

If the plan is consistent, for each query of test T, print in a line the easiest way to obtain the certificate of this test, in the format:

T0->T1->...->T

However, if T is the first level of some subject test (with no prerequisite), print You may take test T directly. instead.

If the plan is impossible, for each query of test T, check if one can take it directly or not. If the answer is yes, print in a line You may take test T directly.; or print Error. instead.

Sample Input 1:

8 15
0 1 50 50
1 2 20 20
3 4 90 90
3 7 90 80
4 5 20 20
7 5 10 10
5 6 10 10
0 4 80 60
3 1 50 45
1 4 30 20
1 5 50 20
2 4 10 10
7 2 10 30
2 5 30 20
2 6 40 60
8
0 1 2 3 4 5 6 7

Sample Output 1:

Okay.
You may take test 0 directly.
0->1
0->1->2
You may take test 3 directly.
0->1->2->4
0->1->2->4->5
0->1->2->6
3->7

Sample Input 2:

4 5
0 1 1 10
1 2 2 10
3 0 4 10
3 2 5 10
2 0 3 10
2
3 1

Sample Output 2:

Impossible.
You may take test 3 directly.
Error.

解析:

先判斷是否DAG,再求路徑。DAG的判斷算法筆記上已經有模板,照抄即可;而對於求路徑,由於起點可能有多個而終點只有一個,如果正向求解,需要遍歷每個入度為0的點,容易導致測試點7超時。因此可以采用兩種變通的方法:1、倒過來,從唯一的終點出發往回DFS,那么就要建立逆鄰接表。2、新建一個頂點作為所有起點的源點,一遍迪傑斯特拉算法即可。

方法一:逆序DFS。

#include <vector>
#include <queue>
#include <iostream>
using namespace std;

const int maxn = 1002, INF = 0x3fffffff;

typedef struct Node {
    int next, dist, voucher;
    Node (int n, int d, int v) : next(n), dist(d), voucher(v) {}
} Node;

vector<Node> G[maxn], G2[maxn];
int degree[maxn] = {0}, visited[maxn];
int N, M, K;
vector<int> route, ans;
int min_dist, max_voucher;

bool DAG () {
    int i, count = 0;
    queue<int> q;
    for (i = 0; i < N; i++) {
        if (degree[i] == 0) {
            q.push(i);
        }
    }
    while (!q.empty()) {
        int index = q.front(); q.pop();
        count++;
        for (i = 0; i < G[index].size(); i++) {
            int next = G[index][i].next;
            degree[next]--;
            if (degree[next] == 0) {
                q.push(next);
            }
        }
    }
    return count == N;
}

void DFS (int index, int dist, int voucher) {
    int i;
    if (G2[index].empty()){
        if (min_dist > dist) {
            min_dist = dist; max_voucher = voucher;
            route.push_back(index);
            ans = route;
            route.pop_back();
        } else if (min_dist == dist && max_voucher < voucher) {
            max_voucher = voucher;
            route.push_back(index);
            ans = route;
            route.pop_back();
        }
        return;
    }
    route.push_back(index);
    visited[index] = 1;
    for (i = 0; i < G2[index].size(); i++) {
        Node node = G2[index][i];
        if (visited[node.next]) continue;
        int new_dist = dist + node.dist, new_voucher = voucher + node.voucher;
        if (new_dist <= min_dist) {
            DFS(node.next, new_dist, new_voucher);
        }
    }
    visited[index] = 0;
    route.pop_back();
}


void test() {
    int i, j, t1, t2, t3, t4;
    scanf("%d %d", &N, &M);
    for (i = 0; i < M; i++) {
        scanf("%d %d %d %d", &t1, &t2, &t3, &t4);
        G[t1].push_back(Node(t2, t3, t4));
        G2[t2].push_back(Node(t1, t3, t4));
        degree[t2]++;
    }
    scanf("%d", &K);

    bool flag = DAG();
    if (flag) printf("Okay.\n");
    else printf("Impossible.\n");

    for (i = 0; i < K; i++) {
        scanf("%d", &t1);
        if (G2[t1].empty()) {
            printf("You may take test %d directly.\n", t1);
            continue;
        }
        if (!flag) printf("Error.\n");
        else {
            min_dist = INF, max_voucher = -1;
            DFS(t1, 0, 0);
            for (j = (int)ans.size() - 1; j >= 0; j--) {
                printf("%d", ans[j]);
                if (j) printf("->");
            }
            printf("\n");
        }
    }
}

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

方法二:一遍迪傑斯特拉。

#include <vector>
#include <unordered_set>
#include <queue>
#include <iostream>

using namespace std;

const int maxn = 1002, INF = 0x3fffffff;

typedef struct Node {
    int to, dis1, dis2;
    Node (int t, int d, int d2) : to(t), dis1(d), dis2(d2) {}
} Node;

int N, M;
vector<Node> G1[maxn];
int visited[maxn] = {0}, ins[maxn] = {0}, dist[maxn], pre[maxn], price[maxn];
unordered_set<int> starts;

bool DAG () {
    queue<int> q;
    for (auto item : starts) {
        q.push(item);
    }
    int count = q.size();
    while (!q.empty()) {
        int index = q.front(); q.pop();
        for (auto& item : G1[index]) {
            ins[item.to]--;
            if (ins[item.to] == 0) {
                q.push(item.to); count++;
            }
        }
    }
    return count == N;
}

void dij () {
    int i, j;
    fill(dist, dist + N + 1, INF);
    dist[N] = 0;
    for (i = 0; i <= N; i++) {
        int cur = -1, min_dist = INF;
        for (j = 0; j <= N; j++) {
            if (visited[j] == 0 && dist[j] < min_dist) min_dist = dist[j], cur = j;
        }
        visited[cur] = 1;
        for (auto& item : G1[cur]) {
            if (visited[item.to]) continue;
            if (dist[cur] + item.dis1 < dist[item.to]) {
                dist[item.to] = dist[cur] + item.dis1;
                price[item.to] = price[cur] + item.dis2;
                pre[item.to] = cur;
            } else if (dist[item.to] == dist[cur] + item.dis1 && price[item.to] < price[cur] + item.dis2) {
                price[item.to] = price[cur] + item.dis2;
                pre[item.to] = cur;
            }
        }
    }
}

void test () {
    int t1, t2, s, d, K;
    int i, j;
    scanf("%d %d", &N, &M);
    for (i = 0; i < M; i++) {
        scanf("%d %d %d %d", &t1, &t2, &s, &d);
        G1[t1].push_back(Node(t2, s, d));
        ins[t2]++;
    }
    scanf("%d", &K);
    for (i = 0; i < N; i++) {
        if (ins[i] == 0) starts.insert(i);
    }
    if (DAG()) {
        printf("Okay.\n");
        for (auto item : starts) {
            G1[N].push_back(Node(item, 0, 0));
        }
        dij();
        for (i = 0; i < K; i++) {
            scanf("%d", &t1);
            if (starts.count(t1)) printf("You may take test %d directly.\n", t1);
            else {
                vector<int> ans;
                j = pre[t1];
                while (j != N) {
                    ans.push_back(j);
                    j = pre[j];
                }
                for (j = ans.size() - 1; j >= 0; j--) {
                    printf("%d->", ans[j]);
                }
                printf("%d\n", t1);
            }
        }
    } else {
        printf("Impossible.\n");
        for (i = 0; i < K; i++) {
            scanf("%d", &t1);
            if (starts.count(t1)) printf("You may take test %d directly.\n", t1);
            else printf("Error.\n");
        }
    }

}

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

總結

編號 標題 分數 類型
7-1 Panda and PP Milk 20 4.7 其他高效技巧與算法
7-2 How Many Ways to Buy a Piece of Land 25 8.1 DFS
7-3 Left-View of Binary Tree 25 9.2 二叉樹的遍歷
7-4 Professional Ability Test 30 10.4 最短路徑+10.6 拓撲排序

按通過率來看,第一題最難。。。本次考試的第一題以一己之力大大拉高了整體難度。千萬別小看第一題,很可能就來個下馬威。第二題前綴和可能不容易想到,【A1046 Shortest Distance】這題也用到了該思想。即使想不出來,萬能的DFS也很好用。第三、四題都是套路題,只是小小變通一下。盡管如此,因為第一題的存在,我願稱本次考試為當年最難。

參考資料

pat2020秋考-7-1 Panda and PP Milk (20分)-熊貓盆盆奶


免責聲明!

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



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