以下是我刷PAT乙级的一些总结,不足之处,请各路大神不吝赐教!
题目:害死人不偿命的(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
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;
}
题目:写出这个数 (20)
读入一个自然数n,计算其各位数字之和,用汉语拼音写出和的每一位数字。
输入格式:每个测试输入包含1个测试用例,即给出自然数n的值。这里保证n小于10的100次幂。
输出格式:在一行内输出n的各位数字之和的每一位,拼音数字间有1 空格,但一行中最后一个拼音数字后没有空格。
输入样例:
1234567890987654321123456789
输出样例:
yi san wu
注意: n的范围是(0,10的100次幂)->非常大的数值!因此不可能用Int/long类型存储,而要使用字符类型从高位到个位逐个读取.
思路:
初始化不同数字对应的拼音 —>便于输出结果
while(输入不为换行) —>输入为字符类型
····sum += 字符-‘0’ —>累加和
子函数递归:
(从个位带高位)提取sum每一位数
倒叙输出每位数字对应的拼音(高位到个位)
小技巧:巧用下标–>利用结果数值作为数组下标输出结果
代码如下:
#include
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]);
}
题目:我要通过!(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
#include
#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;i1;
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; iif(check[i] == 1)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
备注:以上程序逐一读取并处理字符。
循环读取并处理单个字符的时候,要格外注意输入流的情况,及时抛弃多余的字符,尤其是使用break的时候,一定要记得在break跳出循环之前抛弃多余的字符输入。 如果忘记处理多余字符,往往会陷入死循环。
题目:成绩排名 (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
#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"%s %s %d",student[i].name,student[i].number,&student[i].score);
}
for(int i =0;iif(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;
}
题目:继续(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
int main(void)
{
int N;
scanf("%d",&N);
int input[N],aux[N];
for(int i = 0;iscanf("%d",&input[i]);
aux[i] = input[i];
}
for(int i = 0;iwhile(aux[i]>1)
{
if(!(aux[i] % 2)) aux[i] /= 2;
else aux[i] = (aux[i] * 3 + 1) / 2;
for(int j = 0;jif(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;i0;i++)
{
if(i == 0)
printf("%d",input[i]);
else
printf(" %d",input[i]);
}
printf("\n");
return 0;
}