PAT(A) -- 2018.9.8秋季考试-甲级 详细题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/it2153534/article/details/82558155

PAT(A) – 2018.9.8秋季考试-甲级

写在开头的话:甲级考试多是考查数据结构和一些基础的算法,此次也是我第一次参加PAT考试,赛前约刷了90道题(一个月),得到了一个80分以上的成绩,自我感觉得到了一下几方面的提升:

  • 更加熟悉树、图相关数据结构及算法,其中如何利用前中序其二建树,如何最简单的表示邻接表,如何使用dijkstra算法及能记录路径等。
  • 甲级考试中,例如动态规划这种算法基本见不着,多是并查集、BFS、DFS、复杂模拟等,综合来看甲级考试简单于CCF。
  • 还需注意一些经典案例:GCD、素数、分数计算、二叉搜索树、质因子分解等,均是《算法笔记》(胡凡、曾磊,强烈建议考PAT和参加计算机研究生复试同学购买)中的讲解需要掌握的知识点。

题解如下:

1148 Werewolf - Simple Version(20 分)

Werewolf(狼人杀) is a game in which the players are partitioned into two parties: the werewolves and the human beings. Suppose that in a game,

  • player #1 said: “Player #2 is a werewolf.”;
  • player #2 said: “Player #3 is a human.”;
  • player #3 said: “Player #4 is a werewolf.”;
  • player #4 said: “Player #5 is a human.”; and
  • player #5 said: “Player #4 is a human.”.

Given that there were 2 werewolves among them, at least one but not all the werewolves were lying, and there were exactly 2 liers. Can you point out the werewolves?

Now you are asked to solve a harder version of this problem: given that there were Nplayers, with 2 werewolves among them, at least one but not all the werewolves were lying, and there were exactly 2 liers. You are supposed to point out the werewolves.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N(5≤N≤100). Then N lines follow and the i-th line gives the statement of the i-th player (1≤i≤N), which is represented by the index of the player with a positive sign for a human and a negative sign for a werewolf.

Output Specification:

If a solution exists, print in a line in ascending order the indices of the two werewolves. The numbers must be separated by exactly one space with no extra spaces at the beginning or the end of the line. If there are more than one solution, you must output the smallest solution sequence – that is, for two sequences A=a[1],…,a[M] and B=b[1],…,b[M], if there exists 0≤k

Sample Input 1:

5
-2
+3
-4
+5
+4

Sample Output 1:

1 4

Sample Input 2:

6
+6
+3
+1
-5
-2
+4

Sample Output 2 (the solution is not unique):

1 5

Sample Input 3:

5
-2
-3
-4
-5
-1

Sample Output 3:

No Solution

题意: N个玩家玩狼人杀,每个人都会说一个人是好人或者是狼,正数表示好人、负数表示狼,其中必须要注意“Given that there were 2 werewolves among them, at least one but not all the werewolves were lying, and there were exactly 2 liers. ”,这句话的意思是:必有一人和一狼说假话。考试时理解为至少有一狼说谎,因此第一个样例(6分)过不了吗,所以考甲级之前英语的过关啊。

分析:由于数据规模N<=100,果断就3重循环暴力求解了。第一二层循环枚举狼,第三层循环验证N个人说的话是否满足一人一狼说谎的要求。

代码如下:

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

int a[105] = { 0 };
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)cin >> a[i];
    int f1, f2;   // f1记录狼说谎的个数,f2记录民说谎的个数
    for (int i = 1; i<n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            f1 = 0; f2 = 0;
            for (int k = 1; k <= n; k++)
            {

                int dir = abs(a[k]);
                if (k == i || k == j)   // 若是狼,计算说谎次数
                {
                    if (dir == i || dir == j)
                    {
                        if (a[k] > 0)f1++;
                    }
                    else
                    {
                        if (a[k] < 0)f1++;
                    }
                }
                else                  // 若是人,计算说谎次数
                {
                    if (dir == i || dir == j)
                    {
                        if (a[k] > 0)f2++;
                    }
                    else
                    {
                        if (a[k] < 0)f2++;
                    }
                }
            }
            if (f2 == 1 && f1 == 1)  // 关键点
            {
                cout << i << " " << j;
                return 0;
            }
        }
    }
    cout << "No Solution";
    return 0;
}

1149 Dangerous Goods Packaging(25 分)

When shipping goods with containers, we have to be careful not to pack some incompatible goods into the same container, or we might get ourselves in serious trouble. For example, oxidizing agent (氧化剂) must not be packed with flammable liquid (易燃液体), or it can cause explosion.

Now you are given a long list of incompatible goods, and several lists of goods to be shipped. You are supposed to tell if all the goods in a list can be packed into the same container.

Input Specification:

Each input file contains one test case. For each case, the first line gives two positive integers: N (≤104), the number of pairs of incompatible goods, and M (≤100), the number of lists of goods to be shipped.

Then two blocks follow. The first block contains N pairs of incompatible goods, each pair occupies a line; and the second one contains M lists of goods to be shipped, each list occupies a line in the following format:

K G[1] G[2] ... G[K]

where K (≤1,000) is the number of goods and G[i]’s are the IDs of the goods. To make it simple, each good is represented by a 5-digit ID number. All the numbers in a line are separated by spaces.

Output Specification:

For each shipping list, print in a line Yes if there are no incompatible goods in the list, or No if not.

Sample Input:

6 3
20001 20002
20003 20004
20005 20006
20003 20001
20005 20004
20004 20006
4 00001 20004 00002 20003
5 98823 20002 20003 20006 10010
3 12345 67890 23333

Sample Output:

No
Yes
Yes

题意:给出多对不能放在一起的物品,现有多个查询,判断所查询的一堆物品是否能否在一起,能-Yes、否-No。

分析:第一眼以为是并查集,因为PAT确实考得多,结果写着写着发现不是,就一水题,直接上代码。

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

vector<int> tab[100005];  // 记录物品x不能和哪些物品放一起
bool vis[100005];         // 物品冲突表
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        int a,b;
        cin>>a>>b;
        tab[a].push_back(b);   // 记录物品x的冲突物品
        tab[b].push_back(a);
    }
    while(m--)
    {
        int t;
        cin>>t;
        bool f=false;
        memset(vis,0,sizeof(vis));
        for(int i=0;i<t;i++)
        {
            int num;
            cin>>num;
            if(f)continue;  
            if(vis[num])  // 检测是否冲突
            {
                f=true;
                continue; // 千万不能写成break.
            }
            for(int j=0;j<tab[num].size();j++)   // 放入物品后把哪些冲突的置1
            {
                vis[tab[num][j]]=1;
            }
        }
        if(f)cout<<"No";
        else cout<<"Yes";
        cout<<endl;
    }
    return 0;
}

1150 Travelling Salesman Problem(25 分)

The “travelling salesman problem” asks the following question: “Given a list of cities and the distances between each pair of cities, what is the shortest possible route that visits each city and returns to the origin city?” It is an NP-hard problem in combinatorial optimization, important in operations research and theoretical computer science. (Quoted from “https://en.wikipedia.org/wiki/Travelling_salesman_problem“.)

In this problem, you are supposed to find, from a given list of cycles, the one that is the closest to the solution of a travelling salesman problem.

Input Specification:

Each input file contains one test case. For each case, the first line contains 2 positive integers N (2

Output Specification:

For each path, print in a line Path X: TotalDist (Description) where X is the index (starting from 1) of that path, TotalDist its total distance (if this distance does not exist, output NAinstead), and Description is one of the following:

  • TS simple cycle if it is a simple cycle that visits every city;
  • TS cycle if it is a cycle that visits every city, but not a simple cycle;
  • Not a TS cycle if it is NOT a cycle that visits every city.

Finally print in a line Shortest Dist(X) = TotalDist where X is the index of the cycle that is the closest to the solution of a travelling salesman problem, and TotalDist is its total distance. It is guaranteed that such a solution is unique.

Sample Input:

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

Sample Output:

Path 1: 11 (TS simple cycle)
Path 2: 13 (TS simple cycle)
Path 3: 10 (Not a TS cycle)
Path 4: 8 (TS cycle)
Path 5: 3 (Not a TS cycle)
Path 6: 13 (Not a TS cycle)
Path 7: NA (Not a TS cycle)
Shortest Dist(4) = 8

题意:输入一幅图,给你路径,需要判断路径是否访问了所有城市并回到原点(形成环)。并给出三种判别:

  • TS simple cycle:最简单的环,即每个城市仅访问了一遍,抽象为路径长度为n+1;
  • TS cycle:环,每个城市均被访问并形成一个环,抽象为路径长度>n+1;
  • Not a TS cycle:路径不构成环,抽象为路径最后一个城市和第一个城市不相等。

分析:此题是一个高起点低落点的题,主要是一个写判断条件的题,分清三种判别需要满足什么条件。

  • 现给出四个条件,路径是否存在f1、是否成环f2、是否是最简单f3、是否每个城市都访问过f4。

    1、f1或f2或f4不满足都属于–Not a TS cycle

    2、满足f1、f2、f4–TS cycle

    3、满足全部–TS simple cycle

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

int Map[205][205]={0};
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int s,e,dis;
        scanf("%d %d %d",&s,&e,&dis);
        Map[s][e]=dis;
        Map[e][s]=dis;
    }
    scanf("%d",&m);
    int minl=9999999,index=-1;
    for(int i=1;i<=m;i++)
    {
        int t;
        scanf("%d",&t);
        if(t==0)
        {
            printf("0 (Not a TS cycle)\n");
            continue;
        }
        vector<int> line;
        line.resize(t);
        for(int j=0;j<t;j++)scanf("%d",&line[j]);
        printf("Path %d: ",i);
        int sum=0,s=line[0];
        bool vis[205];
        memset(vis,0,sizeof(vis));
        vis[s]=1;
        bool f=true,f2=true,f3=true,f4=true; //f控制输出是否是NA,即所给的路径是否存在
                                             //f2控制是否是环
                                             //f3控制是否是simple
                                             //f4控制是否每个城市都访问过
        for(int j=1;j<t;j++)   // 记录长度及访问过的城市
        {
            if(Map[s][line[j]]==0)
            {
                f=false;
                break;
            }
            else
            {
                sum+=Map[s][line[j]];
                s=line[j];
                vis[s]=1;
            }
        }
        if(line[0]!=line[t-1]||t==1)f2=false;
        if(line.size()!=n+1)f3=false;
        for(int j=1;j<=n;j++)if(!vis[j]){f4=false;break;}
        // 输出
        if(!f)printf("NA (Not a TS cycle)\n");  // 注意if...else if...的短路性
        else if(!f2||!f4)printf("%d (Not a TS cycle)\n",sum);
        else if(!f3&&f4)printf("%d (TS cycle)\n",sum);
        else if(f&&f2&&f3&&f4)printf("%d (TS simple cycle)\n",sum);
        if(f&&f2&&f4&&sum<minl)
        {
            minl=sum;index=i;
        }
    }
    printf("Shortest Dist(%d) = %d",index,minl);
    return 0;
}

1151 LCA in a Binary Tree(30 分)

The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U and V as descendants.

Given any two nodes in a binary tree, you are supposed to find their LCA.

Input Specification:

Each input file contains one test case. For each case, the first line gives two positive integers: M (≤ 1,000), the number of pairs of nodes to be tested; and N (≤ 10,000), the number of keys in the binary tree, respectively. In each of the following two lines, N distinct integers are given as the inorder and preorder traversal sequences of the binary tree, respectively. It is guaranteed that the binary tree can be uniquely determined by the input sequences. Then M lines follow, each contains a pair of integer keys U and V. All the keys are in the range of int.

Output Specification:

For each given pair of U and V, print in a line LCA of U and V is A. if the LCA is found and A is the key. But if A is one of U and V, print X is an ancestor of Y. where X is A and Y is the other node. If U or V is not found in the binary tree, print in a line ERROR: U is not found. or ERROR: V is not found. or ERROR: U and V are not found..

Sample Input:

6 8
7 2 3 4 6 5 1 8
5 3 7 2 6 4 8 1
2 6
8 1
7 9
12 -3
0 8
99 99

Sample Output:

LCA of 2 and 6 is 3.
8 is an ancestor of 1.
ERROR: 9 is not found.
ERROR: 12 and -3 are not found.
ERROR: 0 is not found.
ERROR: 99 and 99 are not found.

题意:给你中序、前序遍历,问你任意两个节点,判断他们最矮的父节点。

分析:刷过PintiA的同学们最喜欢的见到的之一就是利用前序、中序建树,注意此题由于测试规模过大,只能用下标建树,否则会MLE。然后就是找两节点的最矮父节点,用DFS可找到树中任意结点,但是也不能用一个vector记录路径否则也会MLE。故要想办法,如何直接抛出LCA结点。本题还需要记录所有出现过的值,用map即可。

代码如下:

#include <iostream>  
#include <cstring>
#include <vector>
#include <map>  
#include <cmath>  
#include <algorithm> 
using namespace std;

struct Node
{
    Node*lc, *rc;
    int val;
};

map<int, bool>tab;
vector<int> mid, pre;

Node* build(int midl, int midr, int prel, int prer)  // 利用下标建树
{
    Node* root = new Node();
    if (midr < midl || prer < prel)
    {
        root = NULL;
        return root;
    }
    root->val = pre[prel];  // 一定注意不要写成pre[0]
    int index = -1;
    for (int i = midl; i<=midr; i++)
    {
        if (mid[i] == root->val)
        {
            index = i;
            break;
        }
    }
    root->lc = build(midl, index - 1, prel + 1, prel + index - midl);
    root->rc = build(index + 1, midr, prel + index - midl + 1, prer);
    return root;
}


Node* dfs(Node* root, int val1,int val2)  // LCA算法
{
    if (root == NULL)return root;
    if (root->val == val1 || root->val == val2)
    {
        return root;
    }
    Node *l, *r;
    l = dfs(root->lc, val1, val2);
    r = dfs(root->rc, val1, val2);
    if (l&&r)return root;
    else if (l)return l;
    else return r;
}

int main()
{
    Node* root;
    int n, m;
    cin >> n >> m;
    mid.resize(m);
    pre.resize(m);
    for (int i = 0; i<m; i++)
    {
        cin >> mid[i];
        tab[mid[i]] = 1;  // 记录存在的节点
    }
    for (int i = 0; i<m; i++)cin >> pre[i];
    root = new Node();
    root = build(0, m - 1, 0, m - 1);
    for (int i = 0; i<n; i++)
    {
        int a, b;
        cin >> a >> b;
        if (!tab[a] && !tab[b])printf("ERROR: %d and %d are not found.\n", a, b);
        else if (!tab[a]) printf("ERROR: %d is not found.\n", a);
        else if (!tab[b]) printf("ERROR: %d is not found.\n", b);
        else
        {
            Node* lca = dfs(root, a, b);
            int num = lca->val;
            if (num == a)printf("%d is an ancestor of %d.\n", a, b);
            else if (num == b)printf("%d is an ancestor of %d.\n", b, a);
            else printf("LCA of %d and %d is %d.\n", a, b, num);
        }
    }
    return 0;
}

总结:做题解的时候发现还是挺轻松的,也感叹这一个月训练确实学习了很多,个人觉得对于数据结构和算法,要想深刻理解必须去敲。由于第一次考PAT还是有些许紧张,感觉发挥不好,第一题理解错题意,第四题卡了3个MLE,但总体来讲难度适中,和练习题差不多,希望下次参加的时候能拿满分吧。

猜你喜欢

转载自blog.csdn.net/it2153534/article/details/82558155