PAT乙级2019年冬仿真卷(C语言)

PAT乙级冬仿真卷(C语言)解析

在这里插入图片描述


十天前晚上看到PAT乙级考试的时间又延期了,就萌生了线上测试的想法。充值了十元大钱,进行了线上测试。
测试结果说来惭愧,20分钟AC了前三道题,第四道题格式错误,乍一看没有发现格式上的问题,就开始做第五道题了。第五道题在最后两个测试点上卡到考试结束,在第五道题debug过程中顺便解决了第四道题的格式错误。由于测试点5是超时,需要改进算法,测试点6是答案错误,存在bug。所以我一直就杠在测试点6上,始终没想明白测试点6到底是在什么地方报了错。离考试只剩半小时时放弃了思考,怀疑这题目是不是有问题。于是粘贴了网上的C++代码,结果别人的代码通过了。我也懂C++的语法,但是并没有看出测试点6为什么没通过。于是当晚就想弃坑PAT。连续十天没有再上PAT上做题了。直到昨天重新开始OJ,做到PAT乙级1025,才发现这道题是从这改编而来,基本没变。而1025的测试点6,在网上也有很多文章提到“游离结点”这个坑点。这个测试点6我至今都是不服气的。
下面逐题解析。


7-1 2019数列 (15分)

把 2019 各个数位上的数字 2、0、1、9 作为一个数列的前 4 项,用它们去构造一个无穷数列,其中第 n(>4)项是它前 4 项之和的个位数字。例如第 5 项为 2, 因为 2+0+1+9=12,个位数是 2。

本题就请你编写程序,列出这个序列的前 n 项。

输入格式:
输入给出正整数 n(≤1000)。

输出格式:
在一行中输出数列的前 n 项,数字间不要有空格。

输入样例:

10

输出样例:

2019224758

题外话:这个数列中永远不会出现 2018,你能证明吗?

AC代码

  • 题目中的数列构造方式与“斐波那契数列”的构造方式相似。斐波那契数列的第n(>2)为前两项数字之和。
#include<stdio.h>
int main(){
    int N,num[1000]={2,0,1,9};	//初始化前四项
    for(int i=4;i<1000;i++){	//逐项推导
        num[i]=(num[i-1]+num[i-2]+num[i-3]+num[i-4])%10;
    }
    scanf("%d",&N);		//由于有输出前四项的可能,故而干脆将前1000项都推导出,按输入来输出
    for(int i=0;i<N;i++){
        printf("%d",num[i]);
    }
    return 0;
}

在这里插入图片描述

7-2 老鼠爱大米 (20分)

翁恺老师曾经设计过一款 Java 挑战游戏,叫“老鼠爱大米”(或许因为他的外号叫“胖胖鼠”)。每个玩家用 Java 代码控制一只鼠,目标是抢吃尽可能多的大米让自己变成胖胖鼠,最胖的那只就是冠军。

因为游戏时间不能太长,我们把玩家分成 N 组,每组 M 只老鼠同场竞技,然后从 N 个分组冠军中直接选出最胖的冠军胖胖鼠。现在就请你写个程序来得到冠军的体重。

输入格式:
输入在第一行中给出 2 个正整数:N(≤100)为组数,M(≤10)为每组玩家个数。随后 N 行,每行给出一组玩家控制的 M 只老鼠最后的体重,均为不超过 1 0 4 10^4 104的非负整数。数字间以空格分隔。

输出格式:
首先在第一行顺次输出各组冠军的体重,数字间以 1 个空格分隔,行首尾不得有多余空格。随后在第二行输出冠军胖胖鼠的体重。

输入样例:

3 5
62 53 88 72 81
12 31 9 0 2
91 42 39 6 48

输出样例:

88 31 91
91

AC代码

#include<stdio.h>
#include<stdlib.h>
int cmp(void *_a,void *_b){		//qsort的回调函数
    int a=*(int *)_a;
    int b=*(int *)_b;
    return b-a;
}
int main(){
    int N,M;
    scanf("%d %d",&N,&M);
    int temp[N][M],		//原始输入数据
    	cham[N];		//每组冠军
    for(int i=0;i<N;i++){
        cham[i]=-1;		
        for(int j=0;j<M;j++){
            scanf("%d",&temp[i][j]);
            if(temp[i][j]>cham[i])cham[i]=temp[i][j];	//找组冠军
        }
    }
    int No1=-1;		//总冠军
    for(int i=0;i<N;i++){
        if(i!=0)printf(" ");
        printf("%d",cham[i]);
        if(cham[i]>No1)No1=cham[i];		//找总冠军
    }
    printf("\n%d",No1);
    return 0;
}

在这里插入图片描述

7-3 String复读机 (20分)

给定一个长度不超过 1 0 4 10^4 104的、仅由英文字母构成的字符串。请将字符重新调整顺序,按StringString… (注意区分大小写)这样的顺序输出,并忽略其它字符。当然,六种字符的个数不一定是一样多的,若某种字符已经输出完,则余下的字符仍按 String 的顺序打印,直到所有字符都被输出。例如 gnirtSSs 要调整成 StringS 输出,其中 s 是多余字符被忽略。

输入格式:
输入在一行中给出一个长度不超过 1 0 4 10^4 104的、仅由英文字母构成的非空字符串。

输出格式:
在一行中按题目要求输出排序后的字符串。题目保证输出非空。

输入样例:

sTRidlinSayBingStrropriiSHSiRiagIgtSSr

输出样例:

StringStringSrigSriSiSii

AC代码

#include<stdio.h>
int main(){
    char temp;		//接收输入字符
    char chr[6]={'S','t','r','i','n','g'};	//输出字符表
    int  cnt[6]={0};						//对应的字符数量
    while((temp=getchar())!='\n'){			
        switch(temp){		//统计各字符数量
            case 'S':cnt[0]++;break;
            case 't':cnt[1]++;break;
            case 'r':cnt[2]++;break;
            case 'i':cnt[3]++;break;
            case 'n':cnt[4]++;break;
            case 'g':cnt[5]++;break;
            default :break;
        }
    }
    for(int flag;flag;){	//循环遍历输出字符表,相应字符数量有剩余就输出,全部输出完退出循环
        flag=0;
        for(int i=0;i<6;i++){	
            if(cnt[i]!=0){
                printf("%c",chr[i]);
                cnt[i]--;
                flag=1;
            }
        }
    }         
    return 0;
}

在这里插入图片描述

7-4 擅长C (20分)

题目太长,省略。
输入格式:
输入首先给出 26 个英文大写字母 A-Z,每个字母用一个 7×5 的、由 C 和 . 组成的矩阵构成。最后在一行中给出一个句子,以回车结束。句子是由若干个单词(每个包含不超过 10 个连续的大写英文字母)组成的,单词间以任何非大写英文字母分隔

题目保证至少给出一个单词。

输出格式:
对每个单词,将其每个字母用矩阵形式在一行中输出,字母间有一列空格分隔。单词的首尾不得有多余空格。
相邻的两个单词间必须有一空行分隔。输出的首尾不得有多余空行。

AC代码

  • 题中:单词间以任何非大写英文字母分隔。之间可能有多个字符进行单词的分隔。我第一次提交时就被这个卡住了,以为是单个字符分隔,只做了单个字符的排除。后来多次审题才发现可能有这么一个坑。
#include<stdio.h>
int main(){
    char list[26][7][5];	//三维数组存放字母矩阵
    for(int i=0;i<26;i++){
        for(int j=0;j<7;j++){
            for(int k=0;k<5;k++){
                scanf(" %c",&list[i][j][k]);
            }
        }
    }
    getchar();		//吸收字母矩阵后的回车
    char word[12],temp;
    int flag=0;
    while((temp=getchar())!='\n'){		
        int count=0;	//单词长度
        for(count=0;'A'<= temp && temp <='Z';count++){
            word[count]=temp;
            temp=getchar();
        }
        if(count==0)continue;	//非大写英文字母,跳过
        //printf("%s\ncount=%d\n",word,count);
        if(flag)printf("\n\n");    
        for(int j=0;j<7;j++){	//输出单词
            if(j!=0)printf("\n");
            for(int t=0;t<count;t++){
                int some=word[t]-'A';
                if(t!=0)printf(" ");
                for(int k=0;k<5;k++){
                    printf("%c",list[some][j][k]);
                }
                
            }           
            flag=1;    
        }        
        if(temp =='\n'){	//读入单词时因回车而结束退出循环。
            break;
        }
    }
    return 0;
}

在这里插入图片描述

7-5 区块反转 (25分)

给定一个单链表 L,我们将每 K 个结点看成一个区块(链表最后若不足 K 个结点,也看成一个区块),请编写程序将 L 中所有区块的链接反转。例如:给定 L 为 1→2→3→4→5→6→7→8,K 为 3,则输出应该为 7→8→4→5→6→1→2→3。

输入格式:
每个输入包含 1 个测试用例。每个测试用例第 1 行给出第 1 个结点的地址、结点总个数正整数 N (≤ 1 0 5 10^5 105)、以及正整数 K (≤N),即区块的大小。结点的地址是 5 位非负整数,NULL 地址用 −1 表示。

接下来有 N 行,每行格式为:

Address Data Next

其中 Address 是结点地址,Data 是该结点保存的整数数据,Next 是下一结点的地址。

输出格式:
对每个测试用例,顺序输出反转后的链表,其上每个结点占一行,格式与输入相同。

输入样例:

00100 8 3
71120 7 88666
00000 4 99999
00100 1 12309
68237 6 71120
33218 3 00000
99999 5 68237
88666 8 -1
12309 2 33218

输出样例:

71120 7 88666
88666 8 00000
00000 4 99999
99999 5 68237
68237 6 00100
00100 1 12309
12309 2 33218
33218 3 -1

AC代码

  • 这题是PAT1025的变体。
  • 网上看这题的代码不多,但1025的C++代码可以说是“八仙过海,各显神通”了。
  • 再次吐槽测试点6:题目说好了给一个单链表,为啥还有游离结点呢???如果一定要这样测试,题目应该说给一堆结点,而不是说给一个单链表。真的气,之前死磕这个点找不到问题所在,结果居然是这个问题。我真的,瞬间就想弃坑PAT了。
  • 这题是组间反转,1025的是组内反转。我是将这个问题转成排序问题做的,所以本题代码和1025的几乎是一样的。
#include<stdio.h>
#include<stdlib.h>
typedef struct{		//定义结点 
        int addr;
        int data;
        int next;
        int grade1;   //分组编号 
        int grade2;   //组内结点编号 
}list;
int cmp(void *_a,void *_b){		//排序规则 
    list *a=(list *)_a;
    list *b=(list *)_b;
    if(b->grade1 != a->grade1){		//非同组按分组编号降序排列 
        return b->grade1-a->grade1;
    }
    else{		//同组按组内结点编号升序排列 
        return a->grade2-b->grade2;
    }
}
int main(){    
    int N,head,K;
    scanf("%d %d %d",&head,&N,&K);
    list a[100000],b[N];		//原始输入数据,按链表顺序存入数组后的数据	
    for(int i=0;i<N;i++){	//输入数据 
        int addr;	
        scanf(" %d",&addr);	//获得地址
        scanf(" %d %d",&a[addr].data,&a[addr].next); //存入该地址对应下标的数组元素中
    } 
    int cnt=0;   //统计链表中的结点数,即排除游离结点
    b[cnt]=a[head];		//设置头结点
    b[cnt].addr=head;	
    while(b[cnt].next!=-1){	//按链表顺序存入数组b
        int addr=b[cnt].next;	//暂存下个结点地址
    	cnt++;					
        b[cnt]=a[addr];			//对应结点存入b数组中
        b[cnt].addr=addr;		//记录地址
        b[cnt].grade1=cnt/K;	//运算得到组编号,被K除后得数相同的为同一组	
		b[cnt].grade2=cnt%K;    //运算得到组内结点编号,被K除后的余数表示它是组内第几个元素 
    }
    qsort(b,(cnt+1),sizeof(list),cmp);    //按规则排序 
    // for(int i=0;i<cnt;i++){    //修改后继节点信息,这里省时未修改
    //	  b[i].next=b[i+1].addr;
	// }
    for(int i=0;i<cnt;i++){   //输出 
        printf("%05d %d %05d\n",b[i].addr,b[i].data,b[i+1].addr);  
        			//未修改后继结点信息,后继结点是错值,最后一项输出上个结点的后继结点地址
    }
    printf("%05d %d -1",b[cnt].addr,b[cnt].data);
    return 0;
}

在这里插入图片描述


  • 疫情的缘故,开学大概率是无限期推迟了。原先报名的PAT乙级考试也在当时弃坑情绪下退考了。至于以后会不会再去刷PAT甲级,到时候再说吧。
  • 学了C++之后,越发觉得C语言在OJ上的吃力。乙级的难度不大,之后的乙级练习还是继续提交C代码,以加深对C语言的理解,及减少对各种库函数的依赖。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值