2023年PAT乙级秋考 满分代码+题解+考试思路梳理

本次我使用的是c++,但基本上没用到c++的特性,c语言也完全可以用我的思路。

B-1 被3整除

已知一个正整数能被 3 整除,要满足的条件是其各位数字之和能被 3 整除。
本题就请你判断任一给定正整数是否能被 3 整除。

输入格式:

输入第一行给出一个正整数 n(≤10),随后 n 行,每行给出一个正整数。题目保证每个整数都不超过 104 位。

输出格式:

对每个输入的整数,如果能被 3 整除,就在对应的一行中输出 Yes,否则输出 No。随后空 1 格,输出这个整数各位数字的和。

输入样例:

3
1234567890
98765432123
7257482459821364032585428796

输出样例:

Yes 45
No 50
No 136
思路  string存储+暴力。

这一题重点就是每一个整数的位数不超过1e4,已经超过了long long 型所能存储的最大位数,所以我们需要用string型来保存数据。

保存以后遍历数据并计算是否是3的倍数。

c代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main() {
	int n,sum;
	scanf("%d",&n);
	char s[1010];
	for(int i=0; i<n; i++) {
		sum=0;
		scanf("%s",&s);
		for(int j=0; j<strlen(s); j++) {
			sum+=s[j]-'0';
		}
		if(sum%3==0) {
			printf("Yes %d\n",sum);
		} else {
			printf("No %d\n",sum);
		}
	}
	return 0;
}
c++代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,sum;
	cin>>n;
	string s;
	for(int i=0;i<n;i++){
		sum=0;
		cin>>s;
		for(int j=0;j<s.size();j++){
			sum+=s[j]-'0';
		}
		if(sum%3==0){
			cout<<"Yes "<<sum<<endl;
		} else {
			cout<<"No "<<sum<<endl;
		}
	}
	return 0;
}

B-2 生日推算

有一道推理题的原文是这样的:甲、乙、丙、丁、戊的生日是在连续的五天里,先后顺序满足以下条件:

  • 甲比丙大几天,乙就比戊小几天;
  • 丁比戊大 2 天;
  • 丙的生日是在星期三。

问另外四人的生日分别在星期几?

现在我们把条件一般化,任意打乱他们的顺序,任意给定其中一人的生日,请你编写程序推算另外四人的生日分别在星期几。

注意到在上述条件描述中,只有“戊”这个人是没有任何信息直接给出的,其他人都有一个与他人关系或自己生日的描述。

输入格式:

输入分 5 行,每行给出一个人的已知信息。其中:

  • 如果是一个 [1, 7] 区间内的整数,则表示这个人的生日已知。数字 1 到 7 顺次表示星期一到星期日。
  • 如果是一个问号 ?,则表示这个人是原文中的“戊”,没有任何信息直接给出。
  • 如果是 -2,则表示这个人比 ? 对应的人大 2 天。
  • 如果是 -x,则表示这个人比那个生日已知的人大 x 天。
  • 如果是 +x,则表示这个人比 ? 对应的人小 x 天。

输出格式:

在一行中按照输入的顺序给出对应每个人的生日,数字间和行首尾都不要有空格。

输入样例:

3
?
-2
-x
+x

输出样例:

32714

注意:星期一的前一天是星期日。样例输出中,第三个人最大,是星期日过生日。

思路 简单的数学思维

这一题重点就是找到甲乙丙丁戊五人之间的关系。

首先,由题意可知:

然后因为五天是连续的

所以

这就是五天的之间的关系

知道五天之间的关系以后,代码就变得十分简单了。 

c代码
#include<stdio.h>
#include<string>
#include<stdlib.h>
int main() {
	int c[5],h;
	char s[5][2];
	for(int i=0; i<5; i++) {
		scanf("%s",&s[i]);
		//保存已知量
		if(s[i][0]>='1'&&s[i][0]<='7') {
			c[i]=s[i][0]-'0';
			h=c[i];
		}
	}
	//通过已知的数来判断其他的天数
	//重点就是星期一的前一天是星期日
	for(int i=0; i<5; i++) {
		if(s[i][0]>='1'&&s[i][0]<='7') {
			continue;
		}
		if(s[i][0]=='?') {
			if(h!=1)
				c[i]=h-1;
			else
				c[i]=7;
		} else if(s[i][1]=='2') {
			if(h>3)
				c[i]=h-3;
			else
				c[i]=h-3+7;
		} else if(s[i][0]=='-') {
			if(h>2)
				c[i]=h-2;
			else
				c[i]=h-2+7;
		} else if(s[i][0]='+') {
			if(h!=7)
				c[i]=h+1;
			else
				c[i]=1;
		}
	}
	for(int i=0; i<5; i++) {
		printf("%d",c[i]);
	}
	return 0;
}
c++代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int c[5],h;
    string s[5];
    for(int i=0;i<5;i++){
        cin>>s[i];
        //保存已知量
        if(s[i][0]>='1'&&s[i][0]<='7'){
            c[i]=s[i][0]-'0';
            h=c[i];
        }
    }
    //通过已知的数来判断其他的天数
    //重点就是星期一的前一天是星期日
    for(int i=0;i<5;i++){
        if(s[i][0]>='1'&&s[i][0]<='7'){
            continue;
        }
        if(s[i][0]=='?'){
            if(h!=1)
            c[i]=h-1;
            else
            c[i]=7;
        }
        else if(s[i][1]=='2'){
            if(h>3)
            c[i]=h-3;
            else
            c[i]=h-3+7;
        } else if(s[i][0]=='-'){
            if(h>2)
            c[i]=h-2;
            else
            c[i]=h-2+7;
        } else if(s[i][0]='+'){
            if(h!=7)
            c[i]=h+1;
            else
            c[i]=1;
        }
    }
    for(int i=0;i<5;i++){
        cout<<c[i];
    }
    return 0;
}

B-3 计算小达人争霸赛 

“计算小达人”争霸赛开始啦!因为参加比赛的小朋友太多了,老师们来不及人工判题,就请你写个程序来帮忙。你要根据每位小朋友的答题记录判断他/她答对了多少题,并且给出冠军的名字。

输入格式:

输入第一行给出 2 个正整数 N(≤1000)和 M(≤100),分别为参赛小朋友的人数和每人回答的问题数。接下来给出 N 个小朋友的答题记录,格式为:

小朋友姓名
A1 + B1 = C1
A2 + B2 = C2
...
AM + BM = CM

其中小朋友姓名是不含空格、仅由英文字母组成的、不超过 20 个字符的字符串。随后的 M 行,每行给出一个加法算式,算式中的每个数字都是两位数的正整数,数字和加号、等号间都有 1 个空格分隔。

输出格式:

判断每个加法算式是否正确,从而统计出每个小朋友做对了多少题。做对最多题数的是冠军。题目保证冠军只有 1 个。输出的格式为:首先按照输入的顺序,每行输出一个小朋友的姓名和他/她做对的题目数,其间以 1 个空格分隔。最后一行输出:Guan Jun Shi XXX!,这里 XXX 就是冠军的姓名。

输入样例:

4 3
XiaoHua
12 + 34 = 46
21 + 39 = 50
45 + 28 = 12
XiaoMing
22 + 33 = 55
58 + 13 = 71
67 + 30 = 97
XiaoLiang
30 + 20 = 40
18 + 19 = 35
37 + 42 = 77
XiaoHong
25 + 36 = 61
81 + 12 = 93
17 + 48 = 55

输出样例:

XiaoHua 1
XiaoMing 3
XiaoLiang 0
XiaoHong 2
Guan Jun Shi XiaoMing!
 思路 模拟

一个最基本的模拟题

重点 考虑名字之间没有空格和 符号数字之间有空格 需要用不同的方法输入

因为告诉了我们每个数字都是两位数,所以可以直接计算。

c代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main() {
	int n,m,maxn=0;
	char s[15],t[15],tt[15];
	scanf("%d %d",&n,&m);
	for(int i=0; i<n; i++) {
		int sum=0;
		scanf("%s",&s);
		getchar();
		//读取换行符
		for(int j=0; j<m; j++) {
			//因为含有空格 直接gets
			gets(t);
			int a,b,c;
			//直接计算 a b c
			a=10*(t[0]-'0')+(t[1]-'0');
			b=10*(t[5]-'0')+(t[6]-'0');
			c=10*(t[10]-'0')+(t[11]-'0');
			if(a+b==c) {
				sum++;
			}
			//因为只有一个冠军 所以这里用<= 防止只有一个小朋友且得分为0 这样会没有答案
			if(maxn<=sum) {
				strcpy(tt,s);
				maxn=sum;
			}
		}
		printf("%s %d\n",s,sum);
	}
	printf("Guan Jun Shi %s!",tt);
	return 0;
}
c++代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,m,maxn=0;
    string s,t,tt,str="\n";
    cin>>n>>m;
    for(int i=0;i<n;i++){
        int sum=0;
        cin>>s;
        //读取换行符
        getline(cin,str);
        for(int j=0;j<m;j++){
            //因为含有空格 所以直接getline
            getline(cin,t);
            int a,b,c;
            //直接计算 a b c
            a=10*(t[0]-'0')+(t[1]-'0');
            b=10*(t[5]-'0')+(t[6]-'0');
            c=10*(t[10]-'0')+(t[11]-'0');
            if(a+b==c){
                sum++;
            }
            //因为只有一个冠军 所以这里用<= 防止只有一个小朋友且得分为0 这样会没有答案
            if(maxn<=sum){
                tt=s;
                maxn=sum;
            }
        }
        cout<<s<<" "<<sum<<endl;
    }
    cout<<"Guan Jun Shi "<<tt<<"!";
    return 0;
}

B-4 分组数列

一个分组数列是按以下规律定义的:第 1 组包含 1 个数字,为 0×3+1;第 2 组包含 2 个数字,为 0×3+2 和 1×3+2;第 3 组包含 3 个数字,为 1×3+3, 2×3+3 和 3×3+3;以此类推。
一般来说,第 i 组包含 i 个数字,从上一组最后一个数字加 1 开始,每次增 3。例如第 3 组可以理解为:包含 3 个数字,从第 2 组最后一个数字 5 加 1(即 6)开始,增 3 得到第 2 个数字为 9,再增 3 得到第 3 个数字为 12。

所以根据定义,这个序列前 10 个数字就是:1, 2, 5, 6, 9, 12, 13, 16, 19, 22。它们实际上分成了 4 个组,即:{1}, {2, 5}, {6, 9, 12}, {13, 16, 19, 22}。
本题就请你找出任意一个给定数字是这个数列的第几项。

输入格式:

输入第一行首先给出一个正整数 n (≤105),是待查询的数字个数。随后 n 行,每行给出一个查询的正整数,所有数字都不超过 105。

输出格式:

对每个查询的正整数,输出其在数列中的序号(从 1 开始)。如果这个数字不在数列中,则输出 Not Found

输入样例:

4
12
16
5941
87654

输出样例:

6
8
2023
Not Found
思路 数据预处理

对于这种查询问题的题 我们可以先将数据进行预处理 直接把1-1e5分别对于的位置算出来
这样可以极大的节省时间 重新查询会超时。

c代码
#include<stdio.h>
const int N=1e5;
//这里用2*N是为了防止数据超出
int n,sum[2*N],he,tt;
int main() {
	scanf("%d",&n);
	int t=1,j=1;
	he=0;
	//对数据进行预处理
	while(he<=N) {
		he+=1;
		sum[he]=j;
		j++;
		for(int i=1; i<t; i++) {
			he+=3;
			sum[he]=j;
			j++;
		}
		t++;
	}
	//直接查询 不再处理数据
	for(int i=0; i<n; i++) {
		scanf("%d",&tt);
		if(sum[tt]!=0) {
			printf("%d\n",sum[tt]);
		} else {
			printf("Not Found\n");
		}
	}
	return 0;
}
c++代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
//这里用2*N是为了防止数据超出
int n,sum[2*N],he,tt;
int main()
{
    cin>>n;
    int t=1,j=1;
    he=0;
    //对数据进行预处理
    while(he<=N){
        he+=1;
        sum[he]=j;
        j++;
        for(int i=1;i<t;i++){
            he+=3;
            sum[he]=j;
            j++; 
        }
        t++;
    }
    //直接查询 不再处理数据
    for(int i=0;i<n;i++){
        cin>>tt;
        if(sum[tt]!=0){
            cout<<sum[tt]<<endl;
        } else {
            cout<<"Not Found"<<endl;
        }
    }
    return 0;
}

B-5 两头进一头出

某队列允许在其两端进行入队操作,但仅允许在一端进行出队操作。现给定入队的序列,请你判断一系列出队序列是否可能。例如按 1、2、3、4、5 的顺序入队,则 1、3、2、5、4 这样的出队序列是可以得到的,但 5、1、3、2、4 就是不可能得到的。

输入格式:

输入首先在一行中给出两个正整数 N 和 K(≤10),分别是入队元素的个数和待查验的序列个数。随后一行给出 N 个两两不同的整数(每个都不超过 106)组成的入队序列;再跟着 K 行,每行给出由 N 个入队整数组成的出队序列。同行整数间以空格分隔。

输出格式:

对每个需要查验的出队序列,如果是可能的,则在一行中输出 yes,否则输出 no

输入样例:

5 4
10 2 3 4 5
10 3 2 5 4
5 10 3 2 4
2 3 10 4 5
3 5 10 4 2

输出样例:

yes
no
yes
no
思路  模拟+dfs

因为这道题的数据很小(1-n),那么深搜(dfs)的时间复杂度(o!)也满足要求

我的想法:首先题目的进队顺序是固定的 但没有要求入队完以后再出队 可以随时出队
那我们在满足出队顺序要求的时候直接出队 不会影响后面的顺序
这样我们就进行深度搜索 将所有的情况列出来 判断是否有这种情况
通过数组来模拟队列

c代码
#include<stdio.h>
const int N=1e5;
//sum 存储的是入队顺序 t存储的是出队顺序 h存储的是出队的所有可能性
int n,k,sum[11],t[11],h[22];
bool p;
//i代表入队的位数 j代表出队的位数 r代表往左放 l代表往右放
void dfs(int i,int j,int r,int l) {
	//如果满足出队顺序 p为真
	if(j==k) {
		p=true;
		return;
	}
	//如果满足出列的部分条件 直接出列
	if(h[11-r+1]==t[j]) {
		h[11-r+1]=0;
		dfs(i,j+1,r-1,l);
	}
	//只有当上面还能有放的情况下才能继续放
	if(i<n) {
		//如果队列里面没有数
		if(r==0&&l==0) {
			//以11为中心
			h[11]=sum[i];
			dfs(i+1,j,r+1,l);
		}
		//如果现在下面需要的数等于上面的数 那么我们就放在左边
		//这样可以极大的节省时间
		else if(sum[i]==t[j]) {
			h[11-r]=sum[i];
			dfs(i+1,j,r+1,l);
		}
		//如果没有特殊情况
		//这个是放在左边
		h[11-r]=sum[i];
		dfs(i+1,j,r+1,l);
		//这个是放在右边
		h[11+l+1]=sum[i];
		dfs(i+1,j,r,l+1);
	}
	return;
}
int main() {
	scanf("%d %d",&n,&k);
	for(int i=0; i<n; i++) {
		scanf("%d",&sum[i]);
	}
	//假设左端为出口
	for(int i=0; i<k; i++) {
		for(int j=0; j<n; j++) {
			scanf("%d",&t[j]);
		}
		//先放再判断
		//数据初始化
		p=false;
		for(int k=0; k<22; k++) {
			h[k]=0;
		}
		dfs(0,0,0,0);
		if(p) {
			printf("yes\n");
		} else {
			printf("no\n");
		}
	}
	return 0;
}
c++代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
//sum 存储的是入队顺序 t存储的是出队顺序 h存储的是出队的所有可能性
int n,k,sum[11],t[11],h[22];
bool p;
//i代表入队的位数 j代表出队的位数 r代表往左放 l代表往右放
void dfs(int i,int j,int r,int l){
    //如果满足出队顺序 p为真
    if(j==k){
        p=true;
        return;
    }
    //如果满足出列的部分条件 直接出列
    if(h[11-r+1]==t[j]){
 
 
        h[11-r+1]=0;
        dfs(i,j+1,r-1,l);
    }
    //只有当上面还能有放的情况下才能继续放
    if(i<n){
    //如果队列里面没有数
     if(r==0&&l==0){
        //以11为中心 
        h[11]=sum[i];
        dfs(i+1,j,r+1,l);
     }
     //如果现在下面需要的数等于上面的数 那么我们就放在左边
     //这样可以极大的节省时间
     else if(sum[i]==t[j]){
        h[11-r]=sum[i];
        dfs(i+1,j,r+1,l);
     }
     //如果没有特殊情况
     //这个是放在左边
    h[11-r]=sum[i];
    dfs(i+1,j,r+1,l);
    //这个是放在右边
    h[11+l+1]=sum[i];
    dfs(i+1,j,r,l+1); 
    }
     return;
}
int main()
{
    cin>>n>>k;
    for(int i=0;i<n;i++){
        cin>>sum[i];
    }
    //假设左端为出口 
    for(int i=0;i<k;i++){
        for(int j=0;j<n;j++){
            cin>>t[j];
        }
        //先放再判断
        //数据初始化
        p=false;
        for(int k=0;k<22;k++){
            h[k]=0;
        }
        dfs(0,0,0,0);
        if(p){
            cout<<"yes"<<endl;
        } else {
            cout<<"no"<<endl;
        }
    }
    return 0;
}

总结

总体难度不算高,但最后一题比较难。

第一题就考察大数据的存储

第二题就考察的数学思维 考虑到五人之间的天数关系

第三题就考察的模拟,模拟的难度也不算大,需要注意细节

第四题考察的是数据预处理,对于多次询问,我们该如何处理

第五题考察的是dfs/(学长说的是大模拟,可以用set来解决,但我懒得想了)

如果有什么不懂的话或者更好的想法,欢迎评论区讨论!

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值