2020春季 pat 乙级真题以及答案(99分)
第三题 由于粗心大意 在写的时候忘记了要把结果的前导0去掉 导致我扣掉一分QAQ 有点难过
今年受疫情影响 导致这场考试拖了那么久。看题吧!代码要等考完试一周才能拿到,这里先写思路, 到时候我再更新代码
姥姥发布代码了!更新
7-1 对称日 (15分)

央视新闻发了一条微博,指出 2020 年有个罕见的“对称日”,即 2020 年 2 月 2 日,按照 年年年年月月日日 格式组成的字符串 20200202 是完全对称的。
给定任意一个日期,本题就请你写程序判断一下,这是不是一个对称日?
输入格式:
输入首先在第一行给出正整数 N(1<N≤10)。随后 N 行,每行给出一个日期,却是按英文习惯的格式:Month Day, Year。其中 Month 是月份的缩写,对应如下:
一月:Jan
二月:Feb
三月:Mar
四月:Apr
五月:May
六月:Jun
七月:Jul
八月:Aug
九月:Sep
十月:Oct
十一月:Nov
十二月:Dec
Day 是月份中的日期,为 [1, 31] 区间内的整数;Year 是年份,为 [1, 9999] 区间内的整数。
输出格式:
对每一个给定的日期,在一行中先输出 Y 如果这是一个对称日,否则输出 N;随后空一格,输出日期对应的 年年年年月月日日 格式组成的字符串。
输入样例:
5
Feb 2, 2020
Mar 7, 2020
Oct 10, 101
Nov 21, 1211
Dec 29, 1229
输出样例:
Y 20200202
N 20200307
Y 01011010
Y 12111121
N 12291229
解题思路:
我的思路是先把月份映射成数字 然后再把数字转成string 由于存在一些特殊情况 譬如101 10 10得有前导0 所以直接定一string 然后往里面填充0 再进行判断对称
#include<stdio.h>
#include<iostream>
#include<string>
using namespace std;
int mon(string s){
string mm[12]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
for(int i=0;i<12;++i){
if(s==mm[i])return i+1;
}
return 0;
}
bool judge(string str){
int len=str.size()-1;
for(int i=0;i<=len/2;++i){
if(str[i]!=str[len-i])return false;
}
return true;
}
int main()
{
int year,day,n;
scanf("%d",&n);
while(n--){
string ans,s;
cin>>s;
scanf("%d, %d",&day,&year);
int tmp=year*10000+mon(s)*100+day;
ans=to_string(tmp);
while(ans.size()<8) ans="0"+ans;
printf(judge(ans)?"Y %s\n":"N %s\n",ans.c_str());
}
return 0;
}
7-2 超标区间 (20分)

上图是用某科学研究中采集的数据绘制成的折线图,其中红色横线表示正常数据的阈值(在此图中阈值是 25)。你的任务就是把超出阈值的非正常数据所在的区间找出来。例如上图中横轴 [3, 5] 区间中的 3 个数据点超标,横轴上点 9 (可以表示为区间 [9, 9])对应的数据点也超标。
输入格式:
输入第一行给出两个正整数 N(≤10^4 )和 T(≤100),分别是数据点的数量和阈值。第二行给出 N 个数据点的纵坐标,均为不超过 1000 的正整数,对应的横坐标为整数 0 到 N−1。
输出格式:
按从左到右的顺序输出超标数据的区间,每个区间占一行,格式为 [A, B],其中 A 和 B 为区间的左右端点。如果没有数据超标,则在一行中输出所有数据的最大值。
输入样例 1:
11 25
21 15 25 28 35 27 20 24 18 32 23
输出样例 1:
[3, 5]
[9, 9]
输入样例 2:
11 40
21 15 25 28 35 27 20 24 18 32 23
输出样例 2:
35
解题思路:
可以使用两个变量 一个直接记录最大值 还有一个是记录第一次出现的高于阈值的数字 然后转换一下状态 再找出第一次小于阈值的数字,类似于状态机转换过程 还有 最后要做一个判断 因为可能存在 最后一个数字高于阈值
代码:
#include<stdio.h>
int data[100000];
int main()
{
int t,n,flag=1,flag2=0,maxn=-1;
scanf("%d %d",&n,&t);
for(int i=0,tmp;i<n;++i){
scanf("%d",&tmp);
data[i]=tmp;
}
for(int i=0;i<n;++i){
if(maxn<data[i])maxn=data[i];
if(data[i]>t && flag==1){
printf("[%d, ",i);
flag=2;
flag2=1;
}
if(data[i]<=t && flag==2){
printf("%d]\n",i-1);
flag=1;
}
}
if(flag==2)printf("%d]\n",n-1);
if(flag2==0)printf("%d\n",maxn);
return 0;
}
7-3 钱串子的加法 (20分)

人类习惯用 10 进制,可能因为大多数人类有 10 根手指头,可以用于计数。这个世界上有一种叫“钱串子”(学名“蚰蜒”)的生物,有 30 只细长的手/脚,在它们的世界里,数字应该是 30 进制的。本题就请你实现钱串子世界里的加法运算。
输入格式:
输入在一行中给出两个钱串子世界里的非负整数,其间以空格分隔。
所谓“钱串子世界里的整数”是一个 30 进制的数字,其数字 0 到 9 跟人类世界的整数一致,数字 10 到 29 用小写英文字母 a 到 t 顺次表示。
输入给出的两个整数都不超过 10^5位。
输出格式:
在一行中输出两个整数的和。注意结果数字不得有前导零。
输入样例:
2g50ttaq 0st9hk381
输出样例:
11feik2ir
解题思路:
就是这个题 切记考试时候 认真 细心、我是采用模拟加法的过程 最后调试时候停留在这题 所以我把代码带了出来。模拟的思路很简单 还有记得 末尾的特判 因为可能加完之后 还存在进位的情况 所以 判断一下 然后输出
#include<iostream>
using namespace std;
int main()
{
string s1,s2,res;
cin>>s1>>s2;
while(s1.size()>1 && s1[0]=='0')s1.erase(s1.begin());
while(s2.size()>1 && s2[0]=='0')s2.erase(s2.begin());
int len1=s1.size(),len2=s2.size();
int i=len1-1,j=len2-1,flag=0;//flag ½èλ
while(i>=0 || j>=0){
int a=0,b=0;
if(i>=0 && isalpha(s1[i]))a=10+s1[i]-'a';
else if(i>=0 && isdigit(s1[i]))a=s1[i]-'0';
if(j>=0 && isalpha(s2[j]))b=10+s2[j]-'a';
else if(j>=0 && isdigit(s2[j]))b=s2[j]-'0';
char rec;
int rei=a+b+flag;
flag=0;
if(rei>=30){
flag=1;
rei-=30;
}
if(rei<10)rec=rei+'0';
else if(rei>=10)rec=rei-10+'a';
res=rec+res;
if(i>=0)i--;
if(j>=0)j--;
}
if(flag)res="1"+res;
cout<<res<<endl;
return 0;
}
如果有问题交流咨询,可以加入QQ群:673852347
7-4 全素日 (20分)

以上图片来自新浪微博,展示了一个非常酷的“全素日”:2019年5月23日。即不仅20190523本身是个素数,它的任何以末尾数字3结尾的子串都是素数。
本题就请你写个程序判断一个给定日期是否是“全素日”。
输入格式:
输入按照 yyyymmdd 的格式给出一个日期。题目保证日期在0001年1月1日到9999年12月31日之间。
输出格式:
从原始日期开始,按照子串长度递减的顺序,每行首先输出一个子串和一个空格,然后输出 Yes,如果该子串对应的数字是一个素数,否则输出 No。如果这个日期是一个全素日,则在最后一行输出 All Prime!。
输入样例 1:
20190523
输出样例 1:
20190523 Yes
0190523 Yes
190523 Yes
90523 Yes
0523 Yes
523 Yes
23 Yes
3 Yes
All Prime!
输入样例 2:
20191231
输出样例 2:
20191231 Yes
0191231 Yes
191231 Yes
91231 No
1231 Yes
231 No
31 Yes
1 No
解题思路:
看了下 输出结果的格式 认为使用string来处理比较好 因为呢 有前导0这样的情况存在 如果直接变成数字 就有点麻烦了 使用string转int 有现成函数 stoi 然后质数判断 本题数量不大 直接判断即可
#include<stdio.h>
#include<iostream>
using namespace std;
bool judge(int x){
if(x<=1)return false;
for(int i=2;i*i<=x;++i){
if(x%i==0)return false;
}
return true;
}
int main()
{
int flag=0,tmp;
string str;
cin>>str;
while(str.size()>0){
tmp=stoi(str);
if(!judge(tmp)){
flag=1;
printf("%s No\n",str.c_str());
}
else printf("%s Yes\n",str.c_str());
str.erase(str.begin());
}
if(flag==0)cout<<"All Prime!"<<endl;
return 0;
}
如果有问题交流咨询,可以加入QQ群:673852347
7-5 裁判机 (25分)
有一种数字游戏的规则如下:首先由裁判给定两个不同的正整数,然后参加游戏的几个人轮流给出正整数。要求给出的数字必须是前面已经出现的某两个正整数之差,且不能等于之前的任何一个数。游戏一直持续若干轮,中间有写重复或写错的人就出局。
本题要求你实现这个游戏的裁判机,自动判断每位游戏者给出的数字是否合法,以及最后的赢家。
输入格式:
输入在第一行中给出 2 个初始的正整数,保证都在 [1,10
5
] 范围内且不相同。
第二行依次给出参加比赛的人数 N(2≤N≤10)和每个人都要经历的轮次数 M(2≤M≤10^3 )。
以下 N 行,每行给出 M 个正整数。第 i 行对应第 i 个人给出的数字(i=1,⋯,N)。游戏顺序是从第 1 个人给出第 1 个数字开始,每人顺次在第 1 轮给出自己的第 1 个数字;然后每人顺次在第 2 轮给出自己的第 2 个数字,以此类推。
输出格式:
如果在第 k 轮,第 i 个人出局,就在一行中输出 Round #k: i is out.。出局人后面给出的数字不算;同一轮出局的人按编号增序输出。直到最后一轮结束,在一行中输出 Winner(s): W1 W2 … Wn,其中 W1 … Wn 是最后的赢家编号,按增序输出。数字间以 1 个空格分隔,行末不得有多余空格。如果没有赢家,则输出 No winner.。
输入样例 1:
101 42
4 5
59 34 67 9 7
17 9 8 50 7
25 92 43 26 37
76 51 1 41 40
输出样例 1:
Round #4: 1 is out.
Round #5: 3 is out.
Winner(s): 2 4
输入样例 2:
42 101
4 5
59 34 67 9 7
17 9 18 50 49
25 92 58 1 39
102 32 2 6 41
输出样例 2:
Round #1: 4 is out.
Round #3: 2 is out.
Round #4: 1 is out.
Round #5: 3 is out.
No winner.
解题思路:
这题也是甲级里面的题 我当时做的时候 使用了vector + 大数组的处理方式,因为一开始我使用了set判断但是 有一个测试点 一直超时(肯定超时 是n*n的级别,n是已经构造好的数字集合) 所以 想着改成了大数组 一方面判断key是否存在,一方面判 number-key是否存在 这样只需要遍历一次集合即可,这样时间复杂度降低至O(n)。我这里说的是判定时候的耗时 没说整体的时间复杂度
#include<stdio.h>
#include<iostream>
#include<vector>
using namespace std;
int tags[100010];
int main()
{
int n,m;
int a,b;
vector<int>done;
vector<vector<int> >stu;
scanf("%d %d",&a,&b);
done.push_back(a);
done.push_back(b);
tags[a]=tags[b]=1;
scanf("%d %d",&n,&m);
for(int i=0;i<n;++i){
vector<int>vect;
for(int j=0,tmp;j<m;++j){
scanf("%d",&tmp);
vect.push_back(tmp);
}
stu.push_back(vect);
}
vector<int>flag(n,1);
int left=n;
for(int i=0;i<m && left;++i){
for(int j=0;j<n && left;++j){
if(flag[j]==0)continue;//如果已经out了 就不必再判断了
int out=0;
if(tags[stu[j][i]]) out=1;
else{
for(auto it=done.begin();it!=done.end();++it){
if((*it)-stu[j][i]>0 && tags[ (*it)-stu[j][i] ]){
out=2;
break;
}
}
}
if(out!=2){
left--;
flag[j]=0;
printf("Round #%d: %d is out.\n",i+1,j+1);
}
else{
done.push_back(stu[j][i]);
tags[stu[j][i]]=1;
}
}
}
if(left==0)printf("No winner.\n");
else{
printf("Winner(s):");
for(int i=0;i<n;++i)if(flag[i])printf(" %d",i+1);
}
return 0;
}
如果有问题交流咨询,可以加入QQ群:673852347
3285

被折叠的 条评论
为什么被折叠?



