PAT甲级2023夏季考试题解(A-1 Trap,A-2 Queue Using Two Stacks,A-3 Rank of Binary Tree,A-4 Big Number)

很幸运得到了一个满分,最后一题真的是连蒙带猜的AC的,当AC的那瞬间,可以用喜极而泣来形容了,去年十二月也参加了一次甲级,但是才考到89分,不是很满意,因此这次又去参加了一次。但总算得到了一点收获,也算对自己的一点安慰吧。

A-1 Trap

分数 20

作者 陈越

单位 浙江大学

A robot is designed to move on a map from South toward North. When some obstacle is encountered, the robot can only turn toward West and moves until it can turn toward North and continue.

Given a squared map with n×n blocks, a robot can start from any position below the bottom line (South) and its target is to reach the top line (North). (By the way, kindly remind you that the left-hand-side of the map is West and the right-hand-side is East.)

If some obstacles are placed in the map, the robot might get trapped if it starts from certain positions and can never reach the North line. For example, in the situation given by the following figure, the robot will be trapped if it starts from either position 7 or 8.

Your job is to point out those starting positions which will get the robot trapped.

Note: it is assumed that a robot can move out of the map boundary, and all the blocks around the map are open, without any obstacle. Hence if the robot starts from any position out of the West or East boundary, it can certainly reach North without any problem. Therefore we only have to consider the positions between the West and East boundaries (e.g. the positions from 1 to 10 below the South line in the above figure). Besides, as long as the robot can reach the North line, it is considered successful even of it ends up at out of the boundary (e.g. the robot will have to move out of the map if starts from either the positions 1 or 2, but it can still reach the North line).

Input Specification:

Each input file contains one test case. Each case starts from a positive integer n (≤100) in a line, which is the size of the map. Then n lines follow, each contains n characters, which are either 0 for an open block, or 1 for a block with obstacle. The first line corresponds to the North boundary and the last line the South.

Output Specification:

Output in a line all the starting positions which can get the robot trapped, in increasing order. The positions are indexed from West to East, starting from 1. It is guaranteed that there is at least one output.
All the numbers must be separated by 1 space, and there must be no extra space at the beginning or the end of the line.

Sample Input:

10
0000000000
0000111010
1100100011
0000110001
0000000011
0000000000
0100000100
0001000000
0001000000
0001100000

Sample Output:

7 8

分析: 起点从第 n+1开始走,不是从第n行走;每次只有上方有阻碍物时,才可以往左走,
 * 如果左方也有阻碍物那么就走到了死胡同,不能走到最上方;但是到达左边界线
 * 或者上边界线,机器人一定能到达最上面;
 *
 * 根据上面的分析,可以写一个函数来判断从第i列开始走,能否到达上边界线

/**
 * 起点从第 n+1开始走,不是从第n行走;每次只有上方有阻碍物时,才可以往左走,
 * 如果左方也有阻碍物那么就走到了死胡同,不能走到最上方;但是到达左边界线
 * 或者上边界线,机器人一定能到达最上面;
 * 
 * 根据上面的分析,可以写一个函数来判断从第i列开始走,能否到达上边界线
*/

#include <iostream>
#include <set>

using namespace std;

const int N = 110;
char g[N][N];
int n;

bool trap(int y)
{
    bool block = false;
    int x = n+1;

    while(1)
    {
        //到达左边界线或者上边界线,机器人一定能到达最上面
        if(x == 1 || y == 1) 
            break;
        else if(x > 1 && g[x-1][y] == '0') //往北走
            --x;
        else if(y > 1 && g[x][y-1] == '0') //往西走
            --y;
        else
        {
            //上边不能走,左边也不能走,那么肯定遇到了死胡同,
            //不能到达最上边
            block = true; 
            break;
        }
    }

    return block;
}

int main()
{
    fill(*g, *g+N*N, '0');

    cin >> n;
    getchar();

    for(int i=1; i<=n; ++i)
    {
        for(int j=1; j<=n; ++j)
            scanf("%c", &g[i][j]);
        getchar();
    }

    set<int> st;

    for(int i=1; i<=n; ++i)
        if(trap(i) == true)
            st.insert(i);

    bool spa = false;
    for(auto ele : st)
    {
        if(spa) cout << ' ';
        else spa = true;
        cout << ele;
    }

    return 0;
}

A-2 Queue Using Two Stacks

分数 25

作者 陈越

单位 浙江大学

A queue (FIFO structure) can be implemented by two stacks (LIFO structure) in the following way:

  1. Start from two empty stacks s1​ and s2​.
  2. When element e is enqueued, it is actually pushed onto s1​.
  3. When we are supposed to dequeue, s2​ is checked first. If s2​ is empty, everything in s1​ will be transferred to s2​ by popping from s1​ and immediately pushing onto s2​. Then we just pop from s2​ -- the top element of s2​ must be the first one to enter s1​ thus is the first element that was enqueued.

Assume that each operation of push or pop takes 1 unit of time. You job is to tell the time taken for each dequeue.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (≤103), which are the number of operations. Then N lines follow, each gives an operation in the format

Operation Element

where Operation being I represents enqueue and O represents dequeue. For each I, Element is a positive integer that is no more than 106. No Element is given for O operations.
It is guaranteed that there is at least one O operation.

Output Specification:

For each dequeue operation, print in a line the dequeued element and the unites of time taken to do this dequeue. 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.
In case that the queue is empty when dequeue is called, output in a line ERROR instead.

Sample Input:

10
I 20
I 32
O
I 11
O
O
O
I 100
I 66
O

Sample Output:

20 5
32 1
11 3
ERROR
100 5

 

分析:根据题目描述,用两个初始为空的栈来实现队列的入队与出队操作,考试的时候,
 * 题目看了半天没看懂,最后模拟了一遍样例才弄懂了题意。
 *
 * 读懂题意后,这道题就是一个送分题,当然你得会STL,当然数组实现也不麻烦。

/**
 * 根据题目描述,用两个初始为空的栈来实现队列的入队与出队操作,考试的时候,
 * 题目看了半天没看懂,最后模拟了一遍样例才弄懂了题意。
 * 
 * 读懂题意后,这道题就是一个送分题,当然你得会STL,当然数组实现也不麻烦。
*/

#include <iostream>
#include <queue>
#include <stack>

using namespace std;

stack<int> stk1, stk2;

void POP()
{
    if(stk2.empty())
    {
        int sz2 = stk1.size();
        if(stk1.empty())
            puts("ERROR");
        else
        {
            while(stk1.size())
            {
                int u = stk1.top();
                stk1.pop();
                stk2.push(u);
            }
            int u = stk2.top();
            stk2.pop();
            cout << u << ' ' << 2 * sz2 + 1 << endl;
        }
    }
    else
    {
        int u = stk2.top();
        stk2.pop();
        cout << u << ' ' << 1 << endl;
    }
}

int main()
{
    int n;
    cin >> n;
    for(int i=0; i<n; ++i)
    {
        string op;
        int v;
        cin >> op;
        if(op == "I")
        {
            cin >> v;
            stk1.push(v);
        }
        else
            POP();

    }
    return 0;
}

A-3 Rank of Binary Tree

分数 25

作者 陈越

单位 浙江大学

Here we define the rank of a binary tree as n1​×n2​/n0​ where ni​ is the number of nodes of degree i for i=0,1,2.
For example, given a tree shown by the figure, its rank is 2×3/4=1.5.

Given the inorder and preorder traversal sequences of a binary tree, you are supposed to calculate its rank.

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 way we calculate the rank, and the integer part of the rank. The format is:

n1 * n2 / n0 = rank

Sample Input:

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

Sample Output:

2 * 3 / 4 = 1

分析:考察有前序与中序遍历构建一棵二叉树,最后再遍历一遍二叉树,求出度为0,1
 * ,2的结点个数,即能得到答案。

/**
 * 考察有前序与中序遍历构建一棵二叉树,最后再遍历一遍二叉树,求出度为0,1
 * ,2的结点个数,即能得到答案。
*/

#include <iostream>

using namespace std;

typedef struct TNode* Bin;
struct TNode
{
    int v;
    Bin l, r;
};

const int N = 50;
int pre[N], mid[N];
int n0, n1, n2;

Bin Create(int preL, int preR, int midL, int midR)
{
    if(preL > preR) return NULL;

    int root = pre[preL];
    int k;
    for(k=midL; mid[k] != root; ++k);

    int numL = k - midL;

    Bin t = new TNode;
    t->v = pre[preL];
    t->l = Create(preL+1, preL + numL, midL, k-1);
    t->r = Create(preL+numL+1, preR, k+1, midR);
    return t;
}

void pre_Traver(Bin T)
{
    if(T)
    {
        if(T->l && T->r)    n2++;
        else if(!T->l && !T->r) n0++;
        else n1++;
        pre_Traver(T->l);
        pre_Traver(T->r);
    }
}

int main()
{
    int n;
    cin >> n;
    for(int i=0; i<n; ++i)  cin >> mid[i];
    for(int i =0; i<n; ++i)  cin >> pre[i];

    Bin T = Create(0, n-1, 0, n-1);
    pre_Traver(T);

    int res = n1 * n2 / n0;

    printf("%d * %d / %d = %d", n1, n2, n0, res);

    return 0;
}

A-4 Big Number

分数 30

作者 陈越

单位 浙江大学

How to generate a big number of N digits randomly? One way is to find N kids, give each one a card with one's index written on one side (hence it is assumed that the kids are indexed from 1 to N), and ask them to write down a 1-digit number randomly on the other side. Then let the kids pin their digits in a line, on the wall, one by one in ascending order of their indices.

However, it's very difficult to let hundreds of thousands of kids to follow the order. The result is that we have cards pinned randomly all over the wall, some even show the wrong sides. For example, if the 23rd kid has written down 8, we are supposed to find the number 8 on the wall. But instead we might find 23... Your job is to rearrange these cards so that we can obtain the big number as required.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (≤105). Then N lines follow, each describes a card in the format n1 n2 where the two numbers are the numbers written on the two sides of a card.

Output Specification:

For each test case, print in a line the N-digit number as required. That is, print the digits written by the kids in ascending order of their indices. In case that there are 1-digit numbers written on both sides, it would be hard to tell which one is the index and which one is the number written by the kid. Hence the solution may not be unique. In this case, just output the smallest resulting number.

It is guaranteed that a solution exists.

Sample Input:

12
7 11
8 9
3 1
2 12
4 6
10 0
5 1
2 5
6 8
1 4
7 2
9 3

Sample Output:

359114268072

 

分析: 由于卡片编号与卡片数字不知道前后顺序,即第一个数既可能是编号,也有可能
 * 是卡片值;因此需要将两对数对都要保存下来;
 * 但是得注意卡片编号是从1到n进行编号的,当编号为i的卡片只可能有一张时,
 * 那么这张卡片必须属于编号为i的卡片,确定一张卡片以后(不妨设为 {id, val}
 * ,我们就需要将相应的val集合中为id值得卡片给删除掉,一张卡片只能使用一次,
 * 由于需要从集合中删除元素,而N得大小又是1e5,所以需要将删除操作控制在logN
 * 的时间复杂度内,因此卡片用set来存储,但是卡片可能有两张完全相同的数字,
 * (最多也只有两张),所以还需要用 multiset 来存储,对了,还有一个特点是从
 * 第i个集合中找出当前集合中最小的值,multiset自动递增排序,所以选用multiset
 * 是不得已,也是最佳的选择;
 * 当有两张完全相同的卡片的时候,当用完一张,不能将对边的值的集合中的此卡片给
 * 删除掉,因为一删除,就会将两张卡片都删除掉,因此还需要用一个map<PII, int> mp
 * 来记录每张卡片的值的张数。

解释的很糟糕吧,估计过一段时间我来看这段话,也要看半天才能看懂,但还是记录一下的。

/**
 * 由于卡片编号与卡片数字不知道前后顺序,即第一个数既可能是编号,也有可能
 * 是卡片值;因此需要将两对数对都要保存下来;
 * 但是得注意卡片编号是从1到n进行编号的,当编号为i的卡片只可能有一张时,
 * 那么这张卡片必须属于编号为i的卡片,确定一张卡片以后(不妨设为 {id, val}
 * ,我们就需要将相应的val集合中为id值得卡片给删除掉,一张卡片只能使用一次,
 * 由于需要从集合中删除元素,而N得大小又是1e5,所以需要将删除操作控制在logN
 * 的时间复杂度内,因此卡片用set来存储,但是卡片可能有两张完全相同的数字,
 * (最多也只有两张),所以还需要用 multiset 来存储,对了,还有一个特点是从
 * 第i个集合中找出当前集合中最小的值,multiset自动递增排序,所以选用multiset
 * 是不得已,也是最佳的选择;
 * 当有两张完全相同的卡片的时候,当用完一张,不能将对边的值的集合中的此卡片给
 * 删除掉,因为一删除,就会将两张卡片都删除掉,因此还需要用一个map<PII, int> mp
 * 来记录每张卡片的值的张数。
 * 
*/

#include <iostream>
#include <set>
#include <map>
#include <vector>

using namespace std;

typedef pair<int, int>PII;

const int N = 1e5+10;
multiset<int> st[N];
map<PII, int> ump;
bool hs[N];
vector<int> chk;

int main()
{
    int n;
    cin >> n;
    for(int i=0; i<n; ++i)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        if(u > 0 && u <= n)
            st[u].insert(v);
        if(v > 0 && v <= n)
            st[v].insert(u);

        ump[{u, v}]++; //u,v卡片数目加一
        ump[{v, u}]++;
    }

    for(int i=1; i<=n; ++i)
        if(st[i].size() == 1)  //集合i只有一个元素
            chk.push_back(i);

    while(1)
    {
        vector<int> temp;
        if(temp.size()) break;
        for(auto ele : chk)
            if(hs[ele] == 0)
            {
                int v = *st[ele].begin();
                st[v].erase(ele);
                
                //集合v只有一个元素
                if(st[v].size() == 1)   temp.push_back(v);
                hs[ele] = 1; 
            }

        chk = temp;
        if(chk.empty()) break;
    }

    for(int i=1; i<=n; ++i)
    {
        int fs = *st[i].begin();
        printf("%d", fs);

        //当有两张完全相同的卡片的时候,当用完一张,不能将对边的
        //值的集合中的此卡片给删除掉,因为一删除,就会将两张卡片都
        //删除掉,因此还需要用一个map<PII, int> mp来记录每张卡片的
        //值的张数。
        if(ump[{fs, i}] == 1) 
        {
            st[fs].erase(i);
            
            //集合fs只有一个元素
            if(st[fs].size() == 1)
                chk.push_back(fs);
        }

        while(1)
        {
            vector<int> temp;
            if(temp.size()) break;

            for(auto ele : chk)
                if(hs[ele] == 0)
                {
                    int v = *st[ele].begin();
                    st[v].erase(ele);
                    if(st[v].size() == 1)   temp.push_back(v);
                    hs[ele] = 1;
                }

            chk = temp;
            if(chk.empty()) break;
        }
    }
    
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值