最近开始刷PAT乙级的题目,乙级题目共有95道,我计划写5篇博客来总结刷题的过程。这是第1篇博客来记录1001~1019题的心得体会。
分类讨论、STL的简单使用(C with STL)
害死人不偿命的(3n+1)猜想(15 分)
总结:分奇偶讨论、自增计数(count++)
写出这个数(20 分)
1.大数处理
由于C++没有类似于Java的BigInteger,通过将大数(超过long long范围的数)按位存储在数组中。
2.字符数字转化为相应整数
根据ASCII码:字符-0x30即可得到相应数字。
3.输出格式要求
“拼音数字间有 1 空格,但一行中最后一个拼音数字后没有空格”:在每一个拼音的前面加空格,通过设置标志位来控制第一个拼音前没有空格。
我要通过! (20 分)
总结:理解题意是关键,题目关注的是A的数量规律。根据第三条要求可以得出递推公式:a*(b+1)= c+a。
成绩排名 (20 分)
总结:声明一个结构体存储学生的信息,调用sort排序即可。
继续(3n+1)猜想 (25 分)
总结:
按照1001的方法遍历每一个输入的数,记录运算过程中出现的数,即可发现“没有被覆盖的数”。由于无法事先确定有多少个关键数,因此可声明一个vector存储“关键数”(没有被覆盖的数),调用push_back存储和sort排序。
换个格式输出整数 (15 分)
总结:将整数用取模的方法得到各个数位。
素数对猜想 (20 分)
总结:首先使用埃拉托色尼筛选法获取10万以内的素数表(《算法笔记》5.4.2有详细介绍)。之后再依次遍历相邻的两个素数。
数组元素循环右移问题 (20 分)
题干提到:如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?
答:采用一种翻转的方法,如下图函数所示。设数组总长度为N,需右移的长度为M。先对数组的前N-M项进行翻转;再对后M项进行翻转;最后对整个数组(N项)进行翻转,即可得到右移后的数组。
void exchange_begin_end(int arr[], int begin_idx, int end_idx)
{
int mid = (begin_idx+end_idx)/2;
int i;
for(i=begin_idx; i<=mid; i++)
{
int temp = arr[i];
arr[i] = arr[begin_idx+end_idx-i];
arr[begin_idx+end_idx-i] = temp;
}
}
说反话 (20 分)
总结:vector的使用(push_back函数调用)。
思路:循环遍历整个字符串,如果读取到字符将其暂存到一个string类型的tmp中;如果遇到一个空格字符,则把此时的tmp添加到vector中,并把tmp清空。
一元多项式求导 (25 分)
总结:注意题目的“零多项式”是系数指数都为0,才输出0 0。若输入的是5 0或0 4,则什么也不输出。
A+B 和 C (15 分)
总结:由于区间范围最大是2的31次方,因此不能使用int类型声明A、B、C,可使用long类型,对应scanf要用%ld长整型。
数字分类 (20 分)
总结:分5类讨论,5个if简单粗暴;设置5个标志表明是否有此类元素的产生;输出时判断是否有某类元素产生,有则输出,无则输出N。
数素数 (20 分)
总结:同样采用1007中的埃拉托色尼筛选法,与1007区别在于要获得第10000个素数,而不是获得10000以内的素数,1013的计算量更大,需要设置更大的表长,经测试第10000个素数是104729,因此设置步长大于104729即可。
福尔摩斯的约会 (20 分)
总结:在输入的字符串中按规律找出三个信息:星期、小时、分钟。星期和小时是在字符串1和字符串2中寻找,设置day_flag先找星期再找小时。字符串3和字符串4中找第一个相同的字母,分大小写。
德才论 (25 分)
总结:
1.分5类讨论(5个等级),声明一个结构体存储考生的信息(ID、德分、才分以及等级)。注意要将题干中其他达到最低线的考生都归于第4类,第5类是未达到最低线的。
2.分级排序(自定义sort函数的排序规则)
bool cmp_sum(Student x,Student y)
{
if(x.level != y.level)
return x.level < y.level;
else //x.level == y.level
{
if(x.moral+x.intelligence != y.moral+y.intelligence)
return x.moral+x.intelligence > y.moral+y.intelligence;
else //总分相同
{
if(x.moral != y.moral)
return x.moral > y.moral;
else
return x.name < y.name;
}
}
}
部分A+B (15 分)
总结:字符串输入A和B,字符输入DA和DB,方便之后的比较。
A除以B (20 分)
总结:大数除法。
1.声明一个大数的结构体:num存储数字,len表示该数的长度。
struct big_num
{
int num[1001];
int len;
big_num() //Init big_num
{
memset(num,0,sizeof(num));
len = 0;
}
};
2.字符串输入存入big_num中的int类型数组
big_num change(char str[])
{
big_num a;
a.len = strlen(str);
int i;
for(i=0; i<a.len; i++)
{
//整数高位存储在数组的高位
a.num[i] = str[a.len-1-i] - '0';
}
return a;
}
3.大数除法
big_num divide(big_num a, int b, int &r) //a是除数,b是被除数,r是余数
{
big_num c; //商
c.len = a.len;
int i;
for(i=a.len-1; i>=0; i--) //从高位开始进行除法
{
r = r*10 + a.num[i];
if(r<b) //不够除, 商为0
c.num[i] = 0;
else //够除,求商求余
{
c.num[i] = r/b;
r = r % b;
}
}
while(c.len-1>=1 && c.num[c.len-1]==0)
{
//去除高位的0
c.len--;
}
return c;
}
锤子剪刀布 (20 分)
总结:通过遍历并比较输入,分别统计双方胜平负的次数,较为简单;另外需加上两个计数器记录双方获胜时的手势,如下面的代码所示。
int A_Count[3] = {
0}; //0->B 1->C 2->J
int B_Count[3] = {
0};
int get_max_index(int arr[])
{
int max = -1, index = 0;
for(int i=0; i<3; i++) //按照BCJ的顺序进行遍历
{
if(arr[i]>max) //大于max才会变更arr的索引
{
max = arr[i];
index = i;
}
}
return index;
}
//处理后... 输出获胜最多的手势
char arr[3] = {
'B', 'C', 'J'};
printf("%c %c", arr[get_max_index(A_Count)], arr[get_max_index(B_Count)]);
数字黑洞 (20 分)
总结:对一个整数的各个位进行排序,需先把其转化为一个字符串;之后要对这个字符串进行减法运算,需再把其转化为整数。要注意:4位都相同的整数。
//4位整数与字符串之间的互化
int arr_to_int(int arr[])
{
int a;
a = arr[0]*1000 + arr[1]*100 + arr[2]*10 + arr[3];
return a;
}
void int_to_arr(int a)
{
int m = 1;
for(int i=0; i<4; i++)
{
arr[3-i] = (a/m)%10;
m *= 10;
}
}