PAT(甲级)2020年冬季考试

7-1 The Closest Fibonacci Number (20 分)

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

The Fibonacci sequence Fn is defined by Fn+2=Fn+1+Fn for n≥0, with F0=0 and F1=1. The closest Fibonacci number is defined as the Fibonacci number with the smallest absolute difference with the given integer N.

Your job is to find the closest Fibonacci number for any given N.

Input Specification:

Each input file contains one test case, which gives a positive integer N (≤108).

Output Specification:

For each case, print the closest Fibonacci number. If the solution is not unique, output the smallest one.

Sample Input:

305

Sample Output:

233

Hint:

Since part of the sequence is { 0, 1, 1, 2, 3, 5, 8, 12, 21, 34, 55, 89, 144, 233, 377, 610, ... }, there are two solutions: 233 and 377, both have the smallest distance 72 to 305. The smaller one must be printed out.

解析:

方法一:一个接一个算,直到算出来的大于N为止,然后判断根据哪种情况输出。

#include <vector>
#include <iostream>

using namespace std;

vector<int> fa;  // 存Fibonacci序列

void test () {
    int N;
    scanf("%d", &N);
    if (N == 1) {
        printf("1");
        return;
    }
    fa.push_back(0);
    fa.push_back(1);
    fa.push_back(1);
    while (fa.back() < N) {
        fa.push_back(fa.back() + fa[fa.size() - 2]);
    }
    int temp = fa[fa.size() - 2];
    if (fa.back() == N) printf("%d", fa.back());
    else if (fa.back() - N < N - temp) printf("%d", fa.back());
    else printf("%d", temp);
}

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

方法二:直接在本地算出所有小于等于108的斐波那契数,打表。

#include <iostream>
using namespace std;

int fa[41] = {0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986,102334155};

/* 本地试探出10^8以内斐波那契数的最大下标
int fab (int index) {
    if (index <= m) return fa[index];
    int ans = fab(index - 1) + fab(index - 2);
    fa[index] = ans;
    return ans;
}
 */

void test() {
    int i, N;
    scanf("%d", &N);
    for (i = 1; i <= 40; i++) {
        if (N <= fa[i]) break;
    }
    if (N == fa[i]) printf("%d", N);
    else {
        if (N - fa[i - 1] <= fa[i] - N) printf("%d", fa[i - 1]);
        else printf("%d", fa[i]);
    }
}

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

7-2 Subsequence in Substring (25 分)

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

A substring is a continuous part of a string. A subsequence is the part of a string that might be continuous or not but the order of the elements is maintained. For example, given the string atpaaabpabtt, pabt is a substring, while pat is a subsequence.

Now given a string S and a subsequence P, you are supposed to find the shortest substring of S that contains P. If such a solution is not unique, output the left most one.

Input Specification:

Each input file contains one test case which consists of two lines. The first line contains S and the second line P. S is non-empty and consists of no more than 104 lower English letters. P is guaranteed to be a non-empty subsequence of S.

Output Specification:

For each case, print the shortest substring of S that contains P. If such a solution is not unique, output the left most one.

Sample Input:

atpaaabpabttpcat
pat

Sample Output:

pabt

解析:

暴力查找,一个指针指向S,一个指针指向P。只要在S中找到等于P[0]的字符,就开始找以该字符起始的最小子串。递归或者非递归都可以。注意:不要用string的find方法,很慢。

方法一:DFS+剪枝。

#include <iostream>

using namespace std;

string S, P;
int min_start = 0, min_length;

void DFS (int j, int i, int s_start) {  // j指向P,i指向S,s_start表示当前查找的S的子串的开始下标
    if (j == P.length()) {
        if (min_length > i - s_start) {
            min_length = i - s_start;
            min_start = s_start;
        }
        return;
    }
    // 剪枝:S已扫完而P未扫完,或者当前的S子串已经和记录的最小长度相当了
    if (i >= S.length() || i - s_start >= min_length) return;
    // 在S中找下一个等于P[j]的字符
    while (i < S.length() && S[i] != P[j]) i++;
    if (i < S.length()) {
        DFS(j + 1, i + 1, s_start);
    }
}

void test () {
    cin >> S >> P;
    int start;
    min_length = S.length();
    // 只要在S中找到等于P[0]的字符,就开始递归
    for (start = 0; start + P.length() < S.length(); start++) {
        if (S[start] == P[0]) DFS(1, start + 1, start);
    }
    printf("%s", S.substr(min_start, min_length).c_str());
}

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

方法二:非递归。

#include <iostream>
using namespace std;

string S, P;
int min_start, min_length;

void test () {
    int i, j, start;
    cin >> S >> P;
    min_length = S.length();

    for (start = 0; start + P.length() <= S.length(); start++) {
        // 只要在S中找到等于P[0]的字符,就开始找以该字符起始的最小子串
        if (S[start] == P[0]) {
            for (j = 1, i = start + 1; j < P.length() && i < S.length(); j++, i++) {
                while (P[j] != S[i] && i < S.length()) i++;
            }
            if (j == P.length() && min_length > (i - start)) {
                min_length = i - start;
                min_start = start;
            }
        }
    }
    printf("%s", S.substr(min_start, min_length).c_str());
}

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

7-3 File Path (25 分)

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

FP.JPG
The figure shows the tree view of directories in Windows File Explorer. When a file is selected, there is a file path shown in the above navigation bar. Now given a tree view of directories, your job is to print the file path for any selected file.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (≤103), which is the total number of directories and files. Then N lines follow, each gives the unique 4-digit ID of a file or a directory, starting from the unique root ID 0000. The format is that the files of depth d will have their IDs indented by d spaces. It is guaranteed that there is no conflict in this tree structure.

Then a positive integer K (≤100) is given, followed by K queries of IDs.

Output Specification:

For each queried ID, print in a line the corresponding path from the root to the file in the format: 0000->ID1->ID2->...->ID. If the ID is not in the tree, print Error: ID is not found. instead.

Sample Input:

14
0000
 1234
  2234
   3234
    4234
    4235
    2333
   5234
   6234
    7234
     9999
  0001
   8234
 0002
4 9999 8234 0002 6666

Sample Output:

0000->1234->2234->6234->7234->9999
0000->1234->0001->8234
0000->0002
Error: 6666 is not found.

解析:

1、输入各层的文件时,用栈存储各父级目录以及层数,且栈顶为当前目录。每次输入一个新文件时,根据缩进判断该文件的层数,再对比栈顶文件的层数:

如果该文件层数=栈顶文件层数+1,当前目录不变;

如果该文件层数=栈顶文件层数+2,说明要再深一层,把上次输入的文件压入栈;

剩余的情况都是该文件层数≤栈顶文件层数,那就不断出栈直到该文件层数=栈顶文件层数+1为止,即找到了正确的当前目录。

2、各层的文件形成一棵树,在查找各个文件的路径时,既可以从根开始,也可以从该文件向上找到根。对应的,可以建立不同的数据结构。

如果从根开始,就在结点的结构体里开个vector存放各孩子的结点,用DFS或BFS遍历。如果倒过来,结构体里就存放自己的父亲。

方法一:结构体里存放各孩子,自顶向下使用DFS。标准的DFS要遍历所有结点,故增加一个flag,只要找到了路径就可以退出DFS。

#include <vector>
#include <unordered_set>
#include <stack>
#include <iostream>

using namespace std;

typedef struct Node {
    int id, layer;
    vector<Node*> children;
} Node;
stack<Node*> stack1;
int N, target;
Node* root = NULL;
unordered_set<int> nodes;  // 保存所有结点的ID,方便判断ID是否存在
vector<int> path;  // 保存路径
int flag = 0;  // 用于DFS中快速退出,因为不必遍历所有结点

void DFS (Node *node) {
    // 当flag为1时,提前结束遍历
    if (flag == 1) return;
    path.push_back(node->id);
    // 递归边界
    if (node->id == target) {
        for (int i = 0; i < path.size(); i++) {
            printf("%04d", path[i]);
            if (i < path.size() - 1) printf("->");
        }
        printf("
");
        flag = 1;
        return;
    }
    for (auto item : node->children) {
        DFS(item);
    }
    path.pop_back();
}

void test () {
    int i, j, K;
    string input;
    scanf("%d
", &N);
    // 单独处理根结点
    root = new Node;
    root->id = 0; root->layer = 0;
    stack1.push(root);
    nodes.insert(0);
    getline(cin, input);
    for (i = 1; i < N; i++) {
        getline(cin, input);
        // 确定层数
        for (j = 0; j < input.size(); j++) {
            if (input[j] != ' ') break;
        }
        int id = stoi(input.substr(j, 4));
        nodes.insert(id);
        if (j == stack1.top()->layer + 2) {
            stack1.push(stack1.top()->children.back());
        } else if (j <= stack1.top()->layer) {
            while (stack1.top()->layer >= j) stack1.pop();
        }
        // 把当前结点放入父节点的数据结构中
        Node *node = new Node;
        node->layer = j; node->id = id;
        stack1.top()->children.push_back(node);
    }
    scanf("%d", &K);
    for (i = 0; i < K; i++) {
        scanf("%d", &target);
        if (nodes.count(target) == 0) printf("Error: %04d is not found.
", target);
        else {
            path.clear();
            flag = 0;
            DFS(root);
        }
    }
}

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

方法二:从下往上找路径,每个结点结构体里存放父节点的ID。再用一个字典存放所有结点。

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

typedef struct Node {
    int layer;
    string father;
} Node;

map<string, Node> parents;  // 存储整棵树
stack<string> stack1;  // 栈里存放父节点的ID
int N, K;
vector<string> ans;  // 存储路径(逆向)

void test() {
    int i, j;
    string input, last, temp, root;  // last存放上次输入的结点的ID
    scanf("%d
", &N);
    getline(cin, input);
    // 单独处理根结点
    root = input;
    Node node;
    node.layer = 0, node.father = "";
    parents[input] = node;
    stack1.push(input);
    last = input;
    for (i = 1; i < N; i++) {
        getline(cin, input);
        j = 0;
        // 确定层数
        while (input[j] == ' ') j++;
        input = input.substr(j, 4);
        int top_layer = parents[stack1.top()].layer;
        if (j == top_layer + 2) {
            stack1.push(last);
        } else if (j <= top_layer) {
            do {
                stack1.pop();
                top_layer = parents[stack1.top()].layer;
            } while (top_layer + 1 != j);
        }
        // 把父节点的ID放入当前节点的数据结构中
        Node node1;
        node1.layer = j;
        node1.father = stack1.top();
        parents[input] = node1;
        // last存放上次输入的结点的ID
        last = input;
    }
    scanf("%d", &K);
    for (i = 0; i < K; i++) {
        cin >> input;
        if (parents.count(input) == 1) {
            ans.clear();
            temp = input;
            while (temp != root) {
                ans.push_back(temp); temp = parents[temp].father;
            }
            printf("%s", root.c_str());
            for (j = (int)ans.size() - 1; j >= 0; j--) printf("->%s", ans[j].c_str());
            printf("
");
        } else {
            printf("Error: %s is not found.
", input.c_str());
        }
    }
}

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

7-4 Chemical Equation (30 分)

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

A chemical equation is the symbolic representation of a chemical reaction in the form of symbols and formulae, wherein the reactant entities are given on the left-hand side and the product entities on the right-hand side. For example, CH4+2O2=CO2+2H2O means that the reactants in this chemical reaction are methane and oxygen: CH4 and O2, and the products of this reaction are carbon dioxide and water: CO2 and H2O.

Given a set of reactants and products, you are supposed to tell that in which way we can obtain these products, provided that each reactant can be used only once. For the sake of simplicity, we will consider all the entities on the right-hand side of the equation as one single product.

Input Specification:

Each input file contains one test case. For each case, the first line gives an integer N (2≤N≤20), followed by N distinct indices of reactants. The second line gives an integer M (1≤M≤10), followed by M distinct indices of products. The index of an entity is a 2-digit number.

Then a positive integer K (≤50) is given, followed by K lines of equations, in the format:

reactant_1 + reactant_2 + ... + reactant_n -> product

where all the reactants are distinct and are in increasing order of their indices.

Note: It is guaranteed that

  • one set of reactants will not produce two or more different products, i.e. situation like 01 + 02 -> 03 and 01 + 02 -> 04 is impossible;
  • a reactant cannot be its product unless it is the only one on the left-hand side, i.e. 01 -> 01 is always true (no matter the equation is given or not), but 01 + 02 -> 01 is impossible; and
  • there are never more than 5 different ways of obtaining a product given in the equations list.

Output Specification:

For each case, print the equations that use the given reactants to obtain all the given products. Note that each reactant can be used only once.

Each equation occupies a line, in the same format as we see in the inputs. The equations must be print in the same order as the products given in the input. For each product in order, if the solution is not unique, always print the one with the smallest sequence of reactants -- A sequence { a1,⋯,am } is said to be smaller than another sequence { b1,⋯,bn } if there exists 1≤imin(m,n) so that aj=bj for all j<i, and ai<bi.

It is guaranteed that at least one solution exists.

Sample Input:

8 09 05 03 04 02 01 16 10
3 08 03 04
6
03 + 09 -> 08
02 + 08 -> 04
02 + 04 -> 03
01 + 05 -> 03
01 + 09 + 16 -> 03
02 + 03 + 05 -> 08

Sample Output:

02 + 03 + 05 -> 08
01 + 09 + 16 -> 03
04 -> 04

解析:

大模拟题,应该也没啥别的办法,就用DFS慢慢找咯。

1、用unordered_set存放原料,vector存放要求的产物

2、既然每个方程式只有一个产物,用三维数组存放每个产物的原料,方便反查。

vector<vector<int>> input[100];

一维放各元素的ID,二维表示各元素可以通过哪些方式得到,三维则是各个方程式的原料。例如,参考样例,08有三种方式可以得到:

03 + 09 -> 08 ①
02 + 03 + 05 -> 08 ②
08 -> 08 ③

那么按如下方式存放:

input[8][0] = {3, 9};
input[8][1] = {2, 3, 5};
input[8][2] = {8};

尽管题目中按顺序①式在②式前面,但是为了得到符合题目要求(选择原料序列最小的方案)的结果,需要进行排序:(vector本身是可以进行比较的)

sort(input[8].begin(), input[8].end());

这样就得到了如下存放顺序:

input[8][0] = {2, 3, 5};
input[8][1] = {3, 9};
input[8][2] = {8};

3、产物不必作为之后反应的原料。如果把产物放进原料里,可能会报错。

代码:

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

using namespace std;

vector<vector<int>> input[100];
int N, M, K;
vector<int> b;  // 存放要求的产物
unordered_set<int> a;  // 存放原料

int flag1 = 0;
vector<int> ans[10];  // 存放结果

void DFS (int product_index) {
    // 如果flag1为1,结束DFS
    if (flag1 == 1) return;
    // 递归边界,输出结果,结束所有DFS
    if (product_index >= M) {
        flag1 = 1;
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < ans[i].size(); j++) {
                printf("%02d %s ", ans[i][j], j < ans[i].size() - 1 ? "+" : "->");
            }
            printf("%02d
", b[i]);
        }
        return;
    }
    int product = b[product_index];
    int flag = 0;
    // 根据产物反查每种方程式
    for (auto& reactants : input[product]) {
        flag = 0;
        // 对比当前方程式所需原料,判断所给原料能否提供
        for (auto reactant : reactants) {
            if (a.count(reactant) == 0) {
                flag = 1;
                break;
            }
        }
        // 如果所给原料可以满足需求,将方程式记入ans,然后进行下一步DFS(反查下一个产物)
        if (flag == 0) {
            ans[product_index] = reactants;
            // 从所给原料里删除所需原料
            for (auto reactant : reactants) {
                a.erase(reactant);
            }
            DFS(product_index + 1);
            // 恢复所需原料
            for (auto item : reactants) {
                a.insert(item);
            }
        }
    }
}

void test () {
    int i, t1;
    string t2;
    scanf("%d", &N);
    for (i = 0; i < N; i++) {
        scanf("%d", &t1);
        a.insert(t1);
        // 建立诸如 01 -> 01 的方程式
        vector<int> temp(1, t1);
        input[t1].push_back(temp);
    }
    scanf("%d", &M);
    b.resize(M);
    for (i = 0; i < M; i++) scanf("%d", &b[i]);
    scanf("%d", &K);
    // 存储方程式
    for (i = 0; i < K; i++) {
        vector<int> reactants;
        // 读入原料
        while (1) {
            scanf("%d", &t1);
            cin >> t2;
            reactants.push_back(t1);
            if (t2 != "+") break;
        }
        // 读入产物,并对原料排序
        scanf("%d", &t1);
        sort(reactants.begin(), reactants.end());
        input[t1].push_back(reactants);
    }
    // 所有方程式存储完后,对各个方程按原料序列从小到大排序
    for (auto item : b) {
        sort(input[item].begin(), input[item].end());
    }
    // 从第一个要求产物开始DFS
    DFS(0);
}

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

总结

编号 标题 分数 类型
7-1 The Closest Fibonacci Number 20 5.1 简单数学
7-2 Subsequence in Substring 25 3.6 字符串处理
7-3 File Path 25 7.1 栈的应用
7-4 Chemical Equation 30 8.1 DFS

按通过率来看,最后一题最难,其次是第二题。第一题简单,打表也能做。第二题字符串处理,用比较暴力的办法也能做出来。第三题考简单逻辑,不难想。第四题一个是要想到DFS,另一个要处理好数据,我感觉挺麻烦的。根据小伙伴们的反应,有的迅速AK完,有的最后两小时就一直卡在最后一题上了,真就旱的旱死涝的涝死(而我则属于菜死的)。PS:第二、四题都可以用DFS,第三题也能部分用DFS,这真的没问题吗……

原文地址:https://www.cnblogs.com/jushou233/p/14640849.html