PAT乙級試題整理(一)——牛客網15分真題整理
牛客網上 共有真題六套,其中每套題有15分題一道,20分題目3道,25分題目1道,共計100分。考試時要求考生在180分鐘內完成。我之前只學過Java和C#,沒有學過C語言,粗淺學習了一下C語言基本語法,想借刷題這個機會好好體會一下面向過程的設計語言的精髓。這裡計劃: 1.先說題目思路,2.再貼上自己的垃圾程式碼,做一番自我批評,3.然後貼上我認為的經典程式碼,分析一番以便學習。通過這樣的三個步驟希望提高自己的水平,也為後面開始做20分題做準備。
先附上陳越姥姥知乎上的一段回答壓壓驚———>
1006題 部分A+B
解題思路
1.將輸入數字按照字元形式輸入(絕對常考考點)
15分題總會出一些“大資料“給我們來做,這個時候一般的處理方法就是把長長的數字看成一個個字元來讀取到字串中:
char A[10],B[10];//10的10次方是1+十個零,小於1+十個零就是十個九
scanf("%s %s",A,B);//這裡因為A和B都是字元陣列所以不用加&符號,但是一般變數要加
2.將讀取的DA和DB和字串中比較得到PA和PB
就是一個簡單的for迴圈就可以搞定,唯一要注意的點是DA和DB是數字,A和B裡面是字元,字元‘1’變成數字1需要經過轉換,這也是一個容易遺漏的知識點:
1='1'-'0';//其他數字同理
3.輸出PA+PB
貼上第一次做的程式碼
#include<stdio.h>
int splid(int A,int *LA,int NA);
int main() {
int a, b, da, db, na=1, nb=1;
int pa=0, pb=0;
int la[1000], lb[1000];
scanf("%d %d %d %d", &a, &da, &b, &db);
na=splid(a, la, na);
nb=splid(b, lb, nb);
for (int i = 0; i < na; i++) {
if (la[i] == da) {
pa = pa * 10 + da;
}
}
for (int i = 0; i < nb; i++) {
if (lb[i] == db) {
pb = pb * 10 + db;
}
}
printf("%d\n", pa + pb);
return 0;
}
//拆分a,b
int splid(int A,int *LA,int NA) {
NA = 1;
int i = 0;
while (A / 10 >= 1) {
NA++;
LA[i] = A % 10;
A /= 10;
i++;
}
LA[i] = A % 10;
return NA;
}
雖然三十來行,但是很明顯可以更短,輸入時當時並不會直接把數字變成字串,導致多出一個splid函式,複雜!
看到其他同學就很厲害了:靈活、輕巧,但是太整合化,賦值與使用直接二合一,不知道有沒有隱患,看了多遍,對我小白來說真的很精彩
#include <cstdio>
#include <cmath>
int main()
{
long long A,DA,B,DB;
while(scanf("%lld %lld %lld %lld",&A,&DA,&B,&DB)!=EOF)
{
long long PA=0,PB=0;
while(A)
{
if(A%10==DA)
PA=PA*10+DA;
A=A/10;
}
while(B)
{
if(B%10==DB)
PB=PB*10+DB;
B=B/10;
}
printf("%lld\n",PA+PB);
}
return 0;
}
1011題 各位數統計
解題思路
很明顯此題和1006題有很大相似,都是要對各位數字做事情:
一、讀取數字為字串陣列
二、每個非零(!= 0)字元進行判斷
這裡注意非零字元又是新學到的一點,如果用while( XX != 0){ }這樣的結構,迴圈可以及時停止。後面接上switch case或者雙重for迴圈都可以達到掃描每個數字的數量的作用
三、輸出
輸出時候判斷一下該位數字的個數是不是0;如果不是0則輸出。
寫的太醜,貼出程式碼需要勇氣,可見結構異常臃腫,重複相當多,這個也是第一次做的,垃圾程式碼
#include<stdio.h>
void print(int a, int n,int large);
int main() {
char input[1000];
int i = 0;
int num[10] = { 0 };
int large = 9;
scanf("%s", input);
while (input[i])
{
switch (input[i])
{
case '0':
num[0]++; break;
case '1':
num[1]++; break;
case '2':
num[2]++; break;
case '3':
num[3]++; break;
case '4':
num[4]++; break;
case '5':
num[5]++; break;
case '6':
num[6]++; break;
case '7':
num[7]++; break;
case '8':
num[8]++; break;
case '9':
num[9]++; break;
default:
break;
}
i++;
}
for (int j = 9; j > 0; j--) {
if (num[j] != 0) {
large = j;
break;
}
}
print(num[0],0,large);
print(num[1],1,large);
print(num[2],2, large);
print(num[3],3, large);
print(num[4],4, large);
print(num[5],5, large);
print(num[6],6, large);
print(num[7],7, large);
print(num[8],8, large);
print(num[9],9, large);
return 0;
}
void print(int a,int n, int large) {
if (a != 0&& n!=large) {
printf("%d:%d\n", n, a );
}
else if(a != 0 && n == large)
printf("%d:%d", n, a);
}
這裡繼續貼上我認為的經典程式碼,也很簡練,c++寫的不過差別不大,也就是讀取輸入不一樣。
#include <iostream>
#include <string>
using namespace std;
int times[10];
string n;
int main() {
cin >> n;
for (int i = 0; i < n.length(); i++) //不用判斷直接給陣列++,思路精巧!!
times[n[i]-'0']++;
for (int i = 0; i < 10; i++)//輸出思路也值得學習
if(times[i])
printf("%d:%d\n", i, times[i]);
return 0;
}
這裡他所使用的計數方法簡直讓我這個小白茅塞頓開,五星好評!!
1016 程式執行時間
輸入例子:
123 4577973
輸出例子:
12:42:59
題目分析:
題目說的天花亂墜,其實就是兩個數相減、除以100、再轉化成時間的60進位制的這麼一個過程
一、兩數相減除以100得到時間間隔Span
簡單的運算,需要注意要對span這個結果進行四捨五入,剛學到一個四捨五入的辦法很好:
int s = int(span + 0.5);//即加0.5再取整
二、轉化成60進位制
一般的做法就是按照 H-> M-> S的順序來進行就可以了,注意輸出如果小於10要表示為比如3就是03。
自己第一次的程式碼:
#include<stdio.h>
int main() {
int big = 0, little = 0;
int hour, minute, second,span;
double span0;
scanf("%d %d", &little, &big);
span = (big - little) / 100;
span0 = (big - little) / 100.0;
if (span0 > span + 0.4)
span++;
hour = span / 3600;
minute = (span - hour * 3600) / 60;
second = (span - hour * 3600 - minute * 60);
if (hour < 10)
printf("0%d:", hour);
else
printf("%d:",hour);
if (minute < 10)
printf("0%d:", minute);
else
printf("%d:", minute);
if (second < 10)
printf("0%d\n", second);
else
printf("%d\n", second);
return 0;
}
1.這裡用的輸出00:00:00格式用的是笨辦法;
2.四捨五入用的也是笨辦法;
3.反正看起來慘不忍睹
放上別人的好程式碼(還是C++):
#include <iostream>
using namespace std;
int main() {
int c1, c2;
cin >> c1 >> c2;
double t = 1.0*(c2 - c1) / 100;
int h = t / 3600;
int m = t / 60 - h * 60;
int s = int(t + 0.5) % 60; //保證四捨五入,浮點數+0.5再取整
printf("%02d:%02d:%02d", h, m, s);//輸出格式,我曾經試了一次printf("%2d:%2d:%2d",h,m,s),失敗就放棄了這個方法,現在看來真是學的太粗糙
return 0;
}
1021 查驗身份證
題目分析
這道題目資料稍稍有點多,考點還是大資料運算後的單個數字處理,因此還是使用char字串來讀取資料
一、讀取身份證號(18位)
二、按權值求和再取模(取餘數)
三、驗證餘數與最後一位是否匹配,不匹配則輸出
因為都匹配需要輸出all passed,所以這裡可以引入一個判斷標誌,有不匹配就變false,最後是true就輸出all passed
#include<stdio.h>
#include<stdbool.h>
int main() {
int gra[] = { 7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2 };//權重
char M[] = { '1','0','X','9','8','7','6','5','4','3','2' };//尾號
bool isNamal=true;//判別標誌位
int n = 2, graSum[100] = { 0 }, arv[100] = { 0 };//總數,權和,取模
scanf("%d", &n);//曾經第一個錯誤:忘記&
char id[100][18] = { '0' };//第二個錯誤,想成了17位
for (int i = 0; i < n; i++) {
scanf("%s", id[i]);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < 17; j++) {
if (id[i][j] == 'X') {
id[i][j] = '0' + 10;
}
graSum[i] += gra[j] * (id[i][j]-'0');//注意-'0'
}
arv[i] = graSum[i] % 11;
if (M[arv[i]] != id[i][17]) {//第三個錯誤,第二個錯誤改為18以後忘記把這個改成17
isNamal = false;
for (int j = 0; j < 18; j++) {
if (id[i][j] == '0'+10) {
id[i][j] = 'X';
}
printf("%c", id[i][j]);//第四個錯誤,輸出要一個一個輸,所以是%c而不是%s
}
printf("\n");
}
}
if (isNamal == true) {
printf("All passed");
}
return 0;
}
別人的經典程式碼:
#include <stdio.h>
int main()
{
int N;
int weight[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
char ZtoM[] = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
char ID[19];
scanf("%d", &N);
int d, sum, count = 0; /* index, weighted sum and count for legal IDs */
for(int i = 0; i < N; i++)
{
scanf("%s", ID);
for(d = 0, sum = 0; d < 17 && ID[d] >= '0' && ID[d] <= '9'; d++)
sum += (ID[d] - '0') * weight[d];
if(d == 17 && ID[17] == ZtoM[sum % 11]) /* legal ID */
count++;
else /* illegal ID */
puts(ID);
}
if(count == N)
puts("All passed");
return 0;
}
需要注意的是,我在輸入身份證的時候將’X’轉換成了’0’+10(也就是’:’),但是看別的程式並沒有增加這一步,但是他們的編譯也得到了通過,稍有不解。