2019年9月 第一次参加PAT考试体验及题解

我是在暑假才知道有浙江大学PAT这个考试,想着暑假也没什么计划,于是想刷刷题目,锻炼一下自己的code能力,于是计划开始准备,最早七月底做了几道题,到八月十号左右,开始集中注意力刷题,每天刷7-10道题。
到八月底,我已经将PAT甲级的题目全部刷了一遍,当然有几道题不会做,比如A1026 table tennis,这道模拟题真的很头疼。于是我就跳过了,一直到最后借鉴学长的代码,但是也没有完全弄懂/(ㄒoㄒ)/~~
参加这种正规考试,说实话从进考场就开始紧张,上机刚一个小时,就想上厕所,平时多参加一些正式的考试,也可以多多经历,多多锻炼吧。
本次PAT考试我考了95分,总共有150个人考满分吧,我的编程之路和大佬比起来只是刚刚起步。
我刷PAT题目的时候写了一些博客,之后我会将这些题目整理,并且计划给每道题画出算法图,我认为看算法图,然后自己组织写代码,可以更好的锻炼自己的能力,并且算法图更能简单清晰的表示整个题目的解法,让读者更快的了解题目的解法,读代码相对就不那么平易近人?。

一、首先分享以下刷题的经验

1、我是只刷PAT甲级,乙级的题目刷了十道左右,就不再刷了,感觉提升不大。这要根据自己的情况采取合适的策略,可以先去刷乙级的题目,如果感觉简单的话,就直接开始甲级,不然刷一些水的题目,AC似乎有些成就感,但是我觉得可能会浪费宝贵的时间。当然基础不好的,建议从乙级开始,适合新手上路,并且锻炼代码能力。
2、刷PAT都看那个配套书籍《算法笔记》,我也买了,建议买一下,毕竟也没多少钱,但是我看的不太多,主要学习以下数据处理能力,一定要熟练掌握c++的STL,这真的会对你的刷题带来很大的帮助。建议使用c/c++,准确说是c+stl,代码简练,并且速度快,我真的遇到过那种一模一样的代码,c++通过,java超时的情况。
3、刷题要独立完成,如果碰到比较难的题目,至少要思考10到20分钟。千万不要题目稍微难一点,就跳过,这样跳的题目多了,就会给自己的心理带来不好的影响(心情浮躁)。如果确定自己这道题目不会做,那么就跳过去,开始下一道题,也不要直接认为自己不会就上网看前辈的AC代码,这样多了也会给自己带来惰性心理。等到第二天再来刷题的时候,再来看这道题,或许就会了呢!如果还是不会做,就上网搜答案,多看几个人的代码,并且不要复制粘贴,看着学长的代码,自己敲出来,并且从main函数开始敲,理清整个程序的思路。最后稍微做总结,自己这道题为什么没有做出来?如果涉及到一些知识盲区,就看书或者网上搜资料彻底弄懂。

二、以下是本次考试的题目解答

7-1 Forever
7-2 Merging Linked Lists
7-3 Postfix Expression
7-4 Dijkstra Sequence

7-1 Forever (20 分)

“Forever number” is a positive integer A with K digits, satisfying the following constrains:

the sum of all the digits of A is m;
the sum of all the digits of A+1 is n; and
the greatest common divisor of m and n is a prime number which is greater than 2.
Now you are supposed to find these forever numbers.

Input Specification:
Each input file contains one test case. For each test case, the first line contains a positive integer N (≤5). Then N lines follow, each gives a pair of K (3

Output Specification:
For each pair of K and m, first print in a line Case X, where X is the case index (starts from 1). Then print n and A in the following line. The numbers must be separated by a space. If the solution is not unique, output in the ascending order of n. If still not unique, output in the ascending order of A. If there is no solution, output No Solution.

Sample Input:

2
6 45
7 80

Sample Output:

Case 1
10 189999
10 279999
10 369999
10 459999
10 549999
10 639999
10 729999
10 819999
10 909999
Case 2
No Solution

我考试的时候这道题目最后一个case超时,考完试,再想想这道题,竟然会做了,知道错误的点了。
我再考试的时候使用的是直接dfs,这种情况虽然有中断停止的判断,但是最糟糕的情况下m=89,k=9,那么搜索的时间复杂度是99,并且搜了这么多,最后没有找到一个解。

以下是我对这道题目的分析:

关注n和m的关系,m是A的所有数字的和,n是(A+1)的所有数字的和,然后m和n的最大公约数必须是素数,这就有意思了。假如A的最后一位不是9,那么m=n+1,那么一定不能满足条件,因此可以断定,n一定小于m,这就必须是A的末尾是9,导致A+1产生了进位。以下探讨产生了j个进位,m和n的关系。以例题 k=6 m=40 来说

n = m - 9*j + 1
gcd(m,n)是大于2的素数
满足了上述条件,再开始dfs,这时剩下了k-j位,他们的和是n,当最后满足所有条件的时候存储到 
pair<int,int> (n curr*p-1) 其中curr为当前k-j位数字各个位数相加为n,乘尾数p,然后再减一

解体的关键在于,先确定n,再确定A,这样可以大大减小计算量,这些都是我们人脑思考的东西,可以给机器减小很大的计算量,这道题也给了我一个启示,就是对时间复杂度的分析,在写程序的时候,考虑自己的代码,是否对要求范围内的数据不存在超时的情况。比如最糟糕99搜索,这就会导致超时。
以下是代码

#include 
#include 
#include 
#include 
using namespace std;
int N;
int k,m,n,l;
vector<pair<int,int> > v;
bool isPrime(int a)
{
    if(a<=2)
        return false;
    for(int i=2;i*i<=a;i++)
        if(a%i==0)
            return false;
    return true;
}
bool cmp(pair<int,int> a,pair<int,int> b)
{
    if(a.first!=b.first)
        return a.first<b.first;
    return a.second<b.second;
}
int gcd(int a,int b)
{
    if(a%b==0)
        return b;
    else
        return gcd(b,a%b);
}
int power(int a,int b)
{
    int t=1;
    for(int i=0;i<b;i++)
          t*=a;
    return t;
}
void dfs(int left,int len,int curr,int p) //剩下长度为len,相加和为n
{
    if(left==0&&len==0)
    {
        v.push_back(pair<int,int>(n,curr*p-1));
        return;
    }
    if(left<=0||len==0)
        return;
    if(len==l)
    for(int i=1;i<=9;i++) 
         dfs(left-i,len-1,curr*10+i,p);
    else //第一位不能是0,后面的位可以是0
    for(int i=0;i<=9;i++)
         dfs(left-i,len-1,curr*10+i,p);
}
int main()
{
    cin>>N;
    int i,j;
    for(i=1;i<=N;i++)
    {
        v.clear();
        scanf("%d %d",&k,&m);
        printf("Case %d\n",i);
        j=0;
        n=m+1;
        while(n>2&&j<=k)
        {
        	//关键点,先判断n,当n满足条件的情况下,再进行搜索
            if( isPrime( gcd(m,n) ) ) //m和n的最大公约数是素数
                dfs(n,l=k-j,0,power(10,j));
            j++;
            n=m+1-9*j;
        }
    if(v.empty())
        printf("No Solution\n");
    else{
          sort(v.begin(),v.end(),cmp);
        for(int i=0;i<v.size();i++)
        printf("%d %d\n",v[i].first,v[i].second);
        }
    }
}

7-2 Merging Linked Lists (25 分)

Given two singly linked lists L​1=a1→a2→⋯→an−1→an and L2 =b1→b2→⋯→bm−1→bm. If n≥2m, you are supposed to reverse and merge the shorter one into the longer one to obtain a list like a1→a2→bm→a3→a4→bm−1⋯. For example, given one list being 6→7 and the other one 1→2→3→4→5, you must output 1→2→7→3→4→6→5.

Input Specification:

Each input file contains one test case. For each case, the first line contains the two addresses of the first nodes of L1 and L2, plus a positive N (≤105) which is the total number of nodes given. The address of a node is a 5-digit nonnegative integer, and NULL is represented by -1.

Then N lines follow, each describes a node in the format:

Address Data Next

where Address is the position of the node, Data is a positive integer no more than 105, and Next is the position of the next node. It is guaranteed that no list is empty, and the longer list is at least twice as long as the shorter one.

Output Specification:

For each case, output in order the resulting linked list. Each node occupies a line, and is printed in the same format as in the input.

Sample Input:

00100 01000 7
02233 2 34891
00100 6 00001
34891 3 10086
01000 1 02233
00033 5 -1
10086 4 00033
00001 7 -1

Sample Output:

01000 1 02233
02233 2 00001
00001 7 34891
34891 3 10086
10086 4 00100
00100 6 00033
00033 5 -1

两个链表分别放到v1和v2,短的求逆,然后再合并到ans中即可,简单题。

#include 
#include 
#include 
#include 
using namespace std;
struct node{
    int index;
    int data;
    int next;
};
node arr[100005];
vector<node> v1,v2,ans;
int main()
{
    int L1,L2,N;
    cin>>L1>>L2>>N;
    for(int i=0;i<N;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        arr[a].index=a;
        arr[a].data=b;
        arr[a].next=c;
    }
    int curr=L1;
    while(curr!=-1)
    {
        v1.push_back(arr[curr]);
        curr=arr[curr].next;
    }
    curr=L2;
    while(curr!=-1)
    {
        v2.push_back(arr[curr]);
        curr=arr[curr].next;
    }
    int n=v1.size(),m=v2.size();
    if(n>=2*m)
    {
        int t=0;
        reverse(v2.begin(),v2.end());
        for(int i=0;i<n;i++)
        {
            ans.push_back(v1[i]);
            if( (i+1)%2==0 && t<m )
                ans.push_back(v2[t++]);
        }
    }
    else{
        int t=0;
        reverse(v1.begin(),v1.end());
        for(int i=0;i<m;i++)
        {
            ans.push_back(v2[i]);
            if( (i+1)%2==0 && t<n )
                ans.push_back(v1[t++]);
        }
    }
    int i;
    for(i=0;i<ans.size()-1;i++)
        printf("%05d %d %05d\n",ans[i].index,ans[i].data,ans[i+1].index);
    printf("%05d %d -1\n",ans[i].index,ans[i].data);
    return 0;
}

7-3 Postfix Expression (25 分)

Given a syntax tree (binary), you are supposed to output the corresponding postfix expression, with parentheses reflecting the precedences of the operators.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (≤ 20) which is the total number of nodes in the syntax tree. Then N lines follow, each gives the information of a node (the i-th line corresponds to the i-th node) in the format:

data left_child right_child

where data is a string of no more than 10 characters, left_child and right_child are the indices of this node’s left and right children, respectively. The nodes are indexed from 1 to N. The NULL link is represented by −1. The figures 1 and 2 correspond to the samples 1 and 2, respectively.
2019年9月 第一次参加PAT考试体验及题解_第1张图片
2019年9月 第一次参加PAT考试体验及题解_第2张图片

Output Specification:

For each case, print in a line the postfix expression, with parentheses reflecting the precedences of the operators.There must be no space between any symbols.

Sample Input 1:

8
* 8 7
a -1 -1
* 4 1
+ 2 5
b -1 -1
d -1 -1
- -1 6
c -1 -1

Sample Output 1:

(((a)(b)+)((c)(-(d))*)*)

Sample Input 2:

8
2.35 -1 -1
* 6 1
- -1 4
% 7 8
+ 2 3
a -1 -1
str -1 -1
871 -1 -1

Sample Output 2:

(((a)(2.35)*)(-((str)(871)%))+)

这道题目也很不难,我记得PAT题库里面应该有个题目和这个基本一样吧。

#include 
#include 
using namespace std;
struct node{
    string data;
    int left;
    int right;
};
node arr[25];
bool visit[25]={false};
void postfix(int curr)
{
    if(curr==-1)
        return;
    printf("(");
    if( arr[curr].left==-1 && arr[curr].right!=-1 )
    {
        cout<<arr[curr].data;
        postfix(arr[curr].right);
        printf(")");
        return;
    }
    if( arr[curr].left )
        postfix( arr[curr].left );
    if( arr[curr].right )
        postfix( arr[curr].right );
    cout<<arr[curr].data;
    printf(")");
}
int main()
{
    int n;
    cin>>n;
    string str;
    int a,b;
    for(int i=1;i<=n;i++)
    {
        cin>>str>>a>>b;
        arr[i].data=str;
        arr[i].left=a;
        arr[i].right=b;
        if(a!=-1)
            visit[a]=true;
        if(b!=-1)
            visit[b]=true;
    }
    int root=0;
    for(int i=1;i<=n;i++)
        if(!visit[i])
        {
            root=i;
            break;
        }
    postfix(root);
    return 0;
}

7-4 Dijkstra Sequence (30 分)

Dijkstra’s algorithm is one of the very famous greedy algorithms. It is used for solving the single source shortest path problem which gives the shortest paths from one particular source vertex to all the other vertices of the given graph. It was conceived by computer scientist Edsger W. Dijkstra in 1956 and published three years later.

In this algorithm, a set contains vertices included in shortest path tree is maintained. During each step, we find one vertex which is not yet included and has a minimum distance from the source, and collect it into the set. Hence step by step an ordered sequence of vertices, let’s call it Dijkstra sequence, is generated by Dijkstra’s algorithm.

On the other hand, for a given graph, there could be more than one Dijkstra sequence. For example, both { 5, 1, 3, 4, 2 } and { 5, 3, 1, 2, 4 } are Dijkstra sequences for the graph, where 5 is the source. Your job is to check whether a given sequence is Dijkstra sequence or not.

Input Specification:

Each input file contains one test case. For each case, the first line contains two positive integers Nv(≤103) and Ne(≤10​5), which are the total numbers of vertices and edges, respectively. Hence the vertices are numbered from 1 to Nv.

Then Ne lines follow, each describes an edge by giving the indices of the vertices at the two ends, followed by a positive integer weight (≤100) of the edge. It is guaranteed that the given graph is connected.

Finally the number of queries, K, is given as a positive integer no larger than 100, followed by K lines of sequences, each contains a permutationof the Nv vertices. It is assumed that the first vertex is the source for each sequence.

All the inputs in a line are separated by a space.

Output Specification:

For each of the K sequences, print in a line Yes if it is a Dijkstra sequence, or No if not.

Sample Input:

5 7
1 2 2
1 5 1
2 3 1
2 4 1
2 5 2
3 5 1
3 4 1
4
5 1 3 4 2
5 3 1 2 4
2 3 4 5 1
3 2 1 5 4

Sample Output:

Yes
Yes
Yes
No

这道题目是判断所给定的集合元素顺序是否是求解最短路径的集合顺序,我们一般求解是根据序号最小来确定下一个要放入的点,而实际上,所有的距离最短的点都可以放入集合S。
把握住关键点,所有可以调换顺序进入集合的点,它们到源点src的距离是一样的,这是一个充分必要条件,根据这个特性,我可以很轻松获得解。

#include 
#include 
#include 
#include 
using namespace std;
const int maxn=100005;
int v,e;
vector<pair<int,int> > vt[1005];
int arr[1005];
void dijkstra(int src)
{
    bool S[1005]={false};
    int dist[1005];
    vector<int> ans;
    fill(dist,dist+1005,0x7fffffff);
    for(int i=0;i<vt[src].size();i++)
        dist[ vt[src][i].first ] = vt[src][i].second;
    S[src]=true;
    ans.push_back(src);
    dist[src]=0;
    while(true)
    {
        int index=-1,minn=0x7fffffff;
        for(int i=1;i<=v;i++)
            if(!S[i]&&dist[i]<minn)
        {
            index=i;
            minn=dist[i];
        }
        if(index==-1)
            break;
        ans.push_back(index);//放入集合
        S[index]=true;
        for(int i=0;i<vt[index].size();i++)
            if(!S[ vt[index][i].first ] && dist[index] + vt[index][i].second < dist[ vt[index][i].first ] )
                dist[ vt[index][i].first ] = dist[index] + vt[index][i].second;
    }
    bool f=true;
    for(int i=0;i<v;i++)
        if( !( ans[i]==arr[i]|| dist[ arr[i] ] == dist[ ans[i] ] ) )
        {
            f=false;
            break;
        }
    if( f )
        printf("Yes\n");
    else
        printf("No\n");
}
int main()
{
    cin>>v>>e;
    int a,b,c;
    for(int i=0;i<e;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        vt[a].push_back(pair<int,int>(b,c));
        vt[b].push_back(pair<int,int>(a,c));
    }
    int query;
    cin>>query;
    while(query--)
    {
        for(int i=0;i<v;i++)
            scanf("%d",&arr[i]);
        dijkstra(arr[0]);
    }
    return 0;
}

你可能感兴趣的:(PAT)