2021年春季PAT甲级考试

作为一个绩点不高、牌子不硬的退役ACMer,去年冬天在nvpy的鼓励下,决定先试试PAT为考研做准备,于是认认真真把所有20分的题目过了一遍。放寒假以后,就开始练习所有25分的题目,并且每道题做完都总结了博客,供别人学习,也供自己复习。结果时间安排上出现了问题,导致后面没有太多时间写程序,整个2月份到今天,没写过一行代码(大家参与程序设计考试一定要避免这种行为)考试竟是第一次打开编译器写程序。。。最后没能把PAT题库刷一遍,然后将每道题的高质量题解做出来也是PAT考试中的一个遗憾吧。

根据个人做PAT题库的经历,对于还在备考PAT的同学给出以下建议:

  • 认真阅读题目,不放过每一个单词,并且一定要注意题目的数据范围,多考虑边界条件。
  • 思维一定要缜密,书写代码时保持思路清晰和大脑冷静。
  • 对于一些常见的术语单词要理解,否则影响理解题意。
  • 熟练掌握STL、暴力、贪心、字符串处理、基础数据结构(并查集、二叉树)、简单数论(GCD、欧拉筛等基础知识)、基础图论(dfs、bfs、最短路、拓扑排序)等知识。
  • 总体来说,PAT甲级不需要多么高深的算法知识和数据结构,但是会着重考察个人的思维能力和基础知识。因此在平时的训练中应该多做简单题目,提高码力、改善思维。

这次的题目难度应该是比较适中的,没有很多的坑点,英文也能读懂(这很重要!!!)。

第一题
题意:给出n和m,找出由n个不超过m的素数组成的等差数列,若找不到,则输出不超过m的最大素数。
思路:筛出不超过m的所有素数,然后暴力遍历即可。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

const int N=1e5+5;

int prime[N],ans[20];
int n,m,lim;
vector<int> v;

void setv(){
    prime[0]=prime[1]=1;
    for(int i=2;i<=m;i++)
        if(!prime[i]){
            v.push_back(i);
            for(int j=i+i;j<=m;j+=i)
                prime[j]=1;
        }
}

bool judge(int i,int j){
    int dif=v[i]-v[j];
    for(int k=0;k<n;k++)
        if(v[i]-dif*k<0||prime[v[i]-dif*k])
            return false;
    return true;
}

int main()
{
    scanf("%d%d",&n,&m);

    lim=m/n;
    setv();

    int dif=-1;
    for(int i=v.size()-1;i>=0;i--){
        for(int j=i-1;j>=0;j--)
            if((v[i]-v[j]>dif||(v[i]-v[j]==dif&&v[i]-dif*(n-1)>ans[0]))&&judge(i,j)){
                dif=v[i]-v[j];
                int cnt=0;
                for(int k=n-1;k>=0;k--){
                    ans[k]=v[i]-dif*cnt;
                    cnt++;
                }
            }
    }

    if(dif!=-1)
        for(int i=0;i<n;i++)
            printf("%d%c",ans[i],i==n-1? '\n':' ');
    else
        printf("%d\n",v[v.size()-1]);

    return 0;
}

第二题
题意:给出一天内n个人进出实验室的时间,在保证同一时刻实验室只能有一人工作的情况下(前一人离开的时间可以和后一人进入的时间相同),求这天最多可以让几个人进入实验室工作。
思路:将所有时间换算成秒为单位的整数,然后按照出实验室的时间递增进行排序即可。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=2e3+5;

struct Node{
    int st,en;
} per[N];

int gettime(int hh,int mm,int ss){
    return hh*60*60+mm*60+ss;
}

bool cmp(const Node &a,const Node &b){
    if(a.en==b.en) return a.st<b.st;
    return a.en<b.en;
}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int hh,mm,ss;
        scanf("%d:%d:%d",&hh,&mm,&ss);
        per[i].st=gettime(hh,mm,ss);
        scanf("%d:%d:%d",&hh,&mm,&ss);
        per[i].en=gettime(hh,mm,ss);
    }

    sort(per,per+n,cmp);
//    for(int i=0;i<n;i++)
//        printf("%d %d\n",per[i].st,per[i].en);

    int ans=0;
    for(int i=0;i<n;i++){
        int pre=per[i].en,cnt=1;
        for(int j=i+1;j<n;j++){
            if(per[j].st>=pre){
                cnt++;
                pre=per[j].en;
            }
        }
        ans=max(ans,cnt);
    }

    printf("%d\n",ans);

    return 0;
}

第三题
题意:首先将给出的n个数字按照输入顺序构造一个大顶堆,然后依次判断给出的m个询问是否正确。询问内容包括:a是否为根、a和b是否为兄弟节点、a是否为b的左子节点,a是否为b的右子节点、a是否为b的父节点。
思路:从无到有构造大顶堆,其在任何时刻都满足完全二叉树,因此可以使用数组来存树,即根节点的下标为1,第i个节点的左子节点下标为i<<1,右子节点下标为i<<1|1,然后对于每次插入逐层更新到根节点即可。由于询问给出的是每个节点的值,因此在最大堆生成后,可以使用一个map来记录每个节点的值与其下标的映射关系。最后按照询问内容,进行判断即可。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <sstream>
#include <map>

using namespace std;

const int N=1e3+5;

int tr[N];
map<int,int> mp;

void update(int i){
    while(i!=1&&tr[i]>tr[i>>1]){
        swap(tr[i],tr[i>>1]);
        i=i>>1;
    }
}

int main()
{
    //freopen("input.txt","r",stdin);
    int n,m,a;

    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a);
        tr[i]=a;
        update(i);
    }
    for(int i=1;i<=n;i++)
        mp[tr[i]]=i;

    string s;
    getline(cin,s);
    for(int i=0;i<m;i++){
        getline(cin,s);
        //cout<<s<<endl;
        stringstream ss(s);
        int a,b;
        string op;

        ss>>a>>op;
        if(op=="and"){
            ss>>b;
            int ida=mp[a],idb=mp[b];
            if((ida%2==0&&idb==ida+1)||(idb%2==0&&ida==idb+1)) printf("1");
            else printf("0");
        }
        else{
            ss>>op; ss>>op;
            if(op=="root"){
                if(mp[a]!=1) printf("0");
                else printf("1");
            }
            else if(op=="parent"){
                ss>>op; ss>>b;
                if((mp[b]>>1)!=mp[a]) printf("0");
                else printf("1");
            }
            else if(op=="left"){
                ss>>op; ss>>op; ss>>b;
                if(mp[a]!=(mp[b]<<1)) printf("0");
                else printf("1");
            }
            else if(op=="right"){
                ss>>op; ss>>op; ss>>b;
                if(mp[a]!=(mp[b]<<1|1)) printf("0");
                else printf("1");
            }
        }
    }
    printf("\n");

    return 0;
}

第四题
题意:给出n个结点(1~n)和m条路径,卡车从0号节点开始,保证每次都前往距离最近且未曾走过的结点(若距离相等,则去编号较小的点)。将所有能够到达的点全部遍历一次后,求遍历的顺序。如果可以将n个结点都遍历一次,求卡车所走的总路程,否则按照编号递增的顺序将没有遍历的结点输出。
思路:使用并查集可以求出能够到达的结点。使用Floyd可以求出多源最短路。然后从0号节点开始,按照给定的遍历规则进行遍历即可。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;

const int N=2e2+5;
const int inf=0x3f3f3f3f;

int f[N],g[N][N],vis[N];
vector<int> v;

int Find(int a){
    return f[a]==a? f[a]:f[a]=Find(f[a]);
}

void Floyd(int n){
    for(int k=0;k<=n;k++)
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++)
                g[i][j]=min(g[i][k]+g[k][j],g[i][j]);
}

int main()
{
    int n,m;
    memset(g,inf,sizeof(g));
    scanf("%d%d",&n,&m);

    for(int i=0;i<=n;i++)
        f[i]=i;

    for(int i=0;i<m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        g[u][v]=g[v][u]=w;

        int ru=Find(u),rv=Find(v);
        if(ru!=rv){
            f[ru]=rv;
        }
    }

    int cnt=0;
    for(int i=1;i<=n;i++){
        if(Find(i)==Find(0)) cnt++;
        else v.push_back(i);
    }

    vis[0]=1;
    Floyd(n);

    int ans=0,st=0;
    printf("0");
    for(int i=0;i<cnt;i++){
        int to=0,tem=inf;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&g[st][j]<tem){
                tem=g[st][j];
                to=j;
            }
        }
        ans+=tem;
        st=to;
        vis[st]=1;
        printf(" %d",st);
    }
    printf("\n");

    if(cnt<n)
        for(int i=0;i<(int)v.size();i++)
            printf("%d%c",v[i],i==(int)v.size()-1? '\n':' ');
    else printf("%d\n",ans);

    return 0;
}

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值