1. 程式人生 > >PAT 乙級 1001——1005 C語言

PAT 乙級 1001——1005 C語言

以下是我刷PAT乙級的一些總結,不足之處,請各路大神不吝賜教!

1001

題目:害死人不償命的(3n+1)猜想 (15)

卡拉茲(Callatz)猜想:
對任何一個自然數n,如果它是偶數,那麼把它砍掉一半;如果它是奇數,那麼把(3n+1)砍掉一半。這樣一直反覆砍下去,最後一定在某一步得到n=1。卡拉茲在1950年的世界數學家大會上公佈了這個猜想,傳說當時耶魯大學師生齊動員,拼命想證明這個貌似很傻很天真的命題,結果鬧得學生們無心學業,一心只證(3n+1),以至於有人說這是一個陰謀,卡拉茲是在蓄意延緩美國數學界教學與科研的進展……
我們今天的題目不是證明卡拉茲猜想,而是對給定的任一不超過1000的正整數n,簡單地數一下,需要多少步(砍幾下)才能得到n=1?
輸入格式:

每個測試輸入包含1個測試用例,即給出自然數n的值。
輸出格式:輸出從n計算到1需要的步數。
輸入樣例:
3
輸出樣例:
5

思路:
輸入
while( number>1 )
….如果是奇數:……
….如果是偶數:……
….步驟數++
輸出步驟數

程式碼如下:

#include<stdio.h>

int main(void)
{
        int number;
        int step = 0;
        scanf("%d",&number);

        while(number>1)
        {   
                if
(number % 2 == 0) number /= 2; else number = (number * 3 + 1) / 2; step++; } printf("%d\n",step); return 0; }

1002

題目:寫出這個數 (20)

讀入一個自然數n,計算其各位數字之和,用漢語拼音寫出和的每一位數字。
輸入格式:每個測試輸入包含1個測試用例,即給出自然數n的值。這裡保證n小於10的100次冪。
輸出格式

:在一行內輸出n的各位數字之和的每一位,拼音數字間有1 空格,但一行中最後一個拼音數字後沒有空格。
輸入樣例:
1234567890987654321123456789
輸出樣例:
yi san wu

注意: n的範圍是(0,10的100次冪)->非常大的數值!因此不可能用Int/long型別儲存,而要使用字元型別從高位到個位逐個讀取.
思路:
初始化不同數字對應的拼音 —>便於輸出結果
while(輸入不為換行) —>輸入為字元型別
····sum += 字元-‘0’ —>累加和

子函式遞迴:
(從個位帶高位)提取sum每一位數
倒敘輸出每位數字對應的拼音(高位到個位)

小技巧:巧用下標–>利用結果數值作為陣列下標輸出結果

程式碼如下:

#include<stdio.h>

char * output[10] = {"ling","yi","er","san","si","wu","liu","qi","ba","jiu"};
void trans(int);
int main(void)
{       
        char ch;
        int sum = 0;
        while((ch = getchar()) != '\n')
        {       
                sum += ch -'0';
        }

        if(sum <10)//個位數,直接輸出結果
                printf("%s",output[sum]);
        else
        {       
                trans(sum);
                putchar('\n');
        }

        return 0;
}

void trans(int sum)
{
        if(!sum)        return;
        trans(sum/10);
        int t = sum % 10;
        if(sum<10)
                printf("%s",output[t]);
        else
                printf(" %s",output[t]);
}

1003

題目:我要通過!(20)

“答案正確”是自動判題系統給出的最令人歡喜的回覆。本題屬於PAT的“答案正確”大派送 —— 只要讀入的字串滿足下列條件,系統就輸出“答案正確”,否則輸出“答案錯誤”。
得到“答案正確”的條件是:
1. 字串中必須僅有P, A, T這三種字元,不可以包含其它字元;
2. 任意形如 xPATx 的字串都可以獲得“答案正確”,其中 x 或者是空字串,或者是僅由字母 A 組成的字串;
3. 如果 aPbTc 是正確的,那麼 aPbATca 也是正確的,其中 a, b, c 均或者是空字串,或者是僅由字母 A 組成的字串。
現在就請你為PAT寫一個自動裁判程式,判定哪些字串是可以獲得“答案正確”的。

輸入格式: 每個測試輸入包含1個測試用例。第1行給出一個自然數n (<10),是需要檢測的字串個數。接下來每個字串佔一行,字串長度不超過100,且不包含空格。
輸出格式:每個字串的檢測結果佔一行,如果該字串可以獲得“答案正確”,則輸出YES,否則輸出NO。
輸入樣例:
8
PAT
PAAT
AAPATAA
AAPAATAAAA
xPATx
PT
Whatever
APAAATAA
輸出樣例:
YES
YES
YES
YES
NO
NO
NO
NO

題意分析:
真的被這道題目繞暈了······看了半天愣沒看明白要幹什麼······
····主要是第三個條件 :<<如果 aPbTc 是正確的,那麼 aPbATca 也是正確的,其中 a, b, c 均或者是空字串,或者是僅由字母 A 組成的字串>>, 觀察到strlen(b)增加1,則strlen(c)會增加strlen(a)
····歸結起來其實就是要求: T後面A的數量 = P前面A的數量 * P和T之間A的數量( strlen(c) = strlen(a) * strlen(b) )

所以正確答案應該有以下特徵:
1. 不包含除P、A、T以外的其他字元
2. PT之間至少有一個A字元(題目保證有且僅有一對PT)
3. T後面A的數量 = P前面A的數量 * P和T之間A的數量

因為只需要判斷以上三個特性就可以判斷是否“答案正確”,因此下面我們會看到,程式只需要記錄P和T出現的陣列下標,甚至不需要儲存任何字串

注意:
1. 題目是先處理完所有輸入,再一次性輸出的。
因此至少要先把處理結果用陣列儲存起來,再一次性輸出。

#include<stdio.h>
#include<string.h>
#define SIZE 101
int main(void)
{
        int N;
        char ch;
        scanf("%d",&N);
        while(getchar()!='\n')continue;

        int check[N],markP,markT;//mark標記P和T下標
        for(int i =0;i<N;i++)
        {
                check[i] = 1;
                int j = 0;
                while((ch = getchar()) != '\n')
                {
                        if(!strchr("PAT",ch))
                        {
                                check[i] = 0;
                                while(getchar()!= '\n')continue;
                                break;
                        }
                        else if(ch == 'P')
                                markP = j;
                        else if(ch == 'T')
                                markT = j;
                        j++;
                }
                if(check[i])
                {
                        int beforeP = markP;
                        int betweenPT = markT - markP -1;
                        int afterT = j - markT - 1;
                        if(!betweenPT || afterT != betweenPT * beforeP) check[i] = 0;
                }
        }

        //輸出
        for(int i = 0; i<N; i++)
        {
                if(check[i] == 1)
                        printf("YES\n");
                else
                        printf("NO\n");
        }

        return 0;
}

備註:以上程式逐一讀取並處理字元。
迴圈讀取並處理單個字元的時候,要格外注意輸入流的情況,及時拋棄多餘的字元,尤其是使用break的時候,一定要記得在break跳出迴圈之前拋棄多餘的字元輸入。 如果忘記處理多餘字元,往往會陷入死迴圈。

1004

題目:成績排名 (20)

讀入n名學生的姓名、學號、成績,分別輸出成績最高和成績最低學生的姓名和學號。
輸入格式: 每個測試輸入包含1個測試用例,格式為
第1行:正整數n
第2行:第1個學生的姓名 學號 成績
第3行:第2個學生的姓名 學號 成績
… … …
第n+1行:第n個學生的姓名 學號 成績
其中姓名和學號均為不超過10個字元的字串,成績為0到100之間的一個整數,這裡保證在一組測試用例中沒有兩個學生的成績是相同的。

輸出格式:對每個測試用例輸出2行,第1行是成績最高學生的姓名和學號,第2行是成績最低學生的姓名和學號,字串間有1空格。
輸入樣例
3
Joe Math990112 89
Mike CS991301 100
Mary EE990830 95
輸出樣例
Mike CS991301
Joe Math990112

思路:定義結構陣列儲存學生資訊,再一 一對比成績,選出max/min

小技巧:遇到找最值/排序/定位/定位等問題的時候,操作下標/指標往往更加經濟,尤其是當結構體很大的時候

程式碼如下:

#include<stdio.h>
#define SIZE 11

typedef struct{
        char name[SIZE];
        char number[SIZE];
        int score;

}STUDENT;

int main(void)
{
        int max = 0;
        int min = 0;
        int N;
        scanf("%d",&N);
        STUDENT student[N];

        for(int i=0;i<N;i++)
        {
                scanf("%s %s %d",student[i].name,student[i].number,&student[i].score);
        }

        for(int i =0;i<N ;i++)
        {
                if(student[i].score < student[min].score)min = i;
                if(student[i].score > student[max].score)max = i;
        }

        printf("%s %s\n",student[max].name,student[max].number);
        printf("%s %s\n",student[min].name,student[min].number);

        return 0;
}

1005

題目:繼續(3n+1)猜想 (25)

卡拉茲(Callatz)猜想已經在1001中給出了描述。在這個題目裡,情況稍微有些複雜。
當我們驗證卡拉茲猜想的時候,為了避免重複計算,可以記錄下遞推過程中遇到的每一個數。例如對n=3進行驗證的時候,我們需要計算3、5、8、4、2、1,則當我們對n=5、8、4、2進行驗證的時候,就可以直接判定卡拉茲猜想的真偽,而不需要重複計算,因為這4個數已經在驗證3的時候遇到過了,我們稱5、8、4、2是被3“覆蓋”的數。我們稱一個數列中的某個數n為“關鍵數”,如果n不能被數列中的其他數字所覆蓋。
現在給定一系列待驗證的數字,我們只需要驗證其中的幾個關鍵數,就可以不必再重複驗證餘下的數字。你的任務就是找出這些關鍵數字,並按從大到小的順序輸出它們。
輸入格式:每個測試輸入包含1個測試用例,第1行給出一個正整數K(<100),第2行給出K個互不相同的待驗證的正整數n(1,100]的值,數字間用空格隔開
輸出格式:每個測試用例的輸出佔一行,按從大到小的順序輸出關鍵數字。數字間用1個空格隔開,但一行中最後一個數字後沒有空格。
輸入樣例:
6
3 5 6 7 8 11
輸出樣例:
7 6

思路:題目中描述的關鍵數有些複雜,但是轉述一下就很簡單了: 沒有被覆蓋的數就是關健數字
因此只要和遞推中間數相等的輸入都標記為“覆蓋”,而其他未被覆蓋的數字則為“關健數”

程式碼如下:

#include<stdio.h>

int main(void)
{
        int N;
        scanf("%d",&N);
        int input[N],aux[N];
        for(int i = 0;i<N;i++)
        {   
                scanf("%d",&input[i]);
                aux[i]  = input[i];

        }   

        for(int i = 0;i<N;i++)
        {   
                while(aux[i]>1)
                {   
                        if(!(aux[i] % 2))       aux[i] /= 2;
                        else                    aux[i] = (aux[i] * 3 + 1) / 2;

                        for(int j = 0;j<N;j++)
                        {
                                if(aux[i] == input[j])
                                {
                                        input[j] = 0;//被覆蓋,置零
                                }
                        }

                }
        }

        //降序排序
        for(int i = 1;i < N;i++)
        {
                for(int j = i;j>0 && input[j]>input[j -1];j--)
                {
                        int t = input[j];
                        input[j] = input[j -1];
                        input[j -1] = t;
                }
        }
        //輸出
        for(int i = 0;i<N && input[i]!=0;i++)
        {
                if(i == 0)
                        printf("%d",input[i]);
                else
                        printf(" %d",input[i]);
        }
        printf("\n");

        return 0;
}