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 只老鼠最後的體重,均爲不超過10410^4的非負整數。數字間以空格分隔。

輸出格式:
首先在第一行順次輸出各組冠軍的體重,數字間以 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分)

給定一個長度不超過10410^4的、僅由英文字母構成的字符串。請將字符重新調整順序,按StringString… (注意區分大小寫)這樣的順序輸出,並忽略其它字符。當然,六種字符的個數不一定是一樣多的,若某種字符已經輸出完,則餘下的字符仍按 String 的順序打印,直到所有字符都被輸出。例如 gnirtSSs 要調整成 StringS 輸出,其中 s 是多餘字符被忽略。

輸入格式:
輸入在一行中給出一個長度不超過10410^4的、僅由英文字母構成的非空字符串。

輸出格式:
在一行中按題目要求輸出排序後的字符串。題目保證輸出非空。

輸入樣例:

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 (≤10510^5)、以及正整數 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語言的理解,及減少對各種庫函數的依賴。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章