PAT乙级2021秋季复盘

【2021.9.11】PAT乙级秋季60分题解(附满分代码)

通过截图

前言

1、这次比赛总体感觉不难,因为此次比赛是本人第一次参加线上赛,经验较少,赛前多次调试电脑和手机摄像头是否正常工作,(因为双机位监考嘛)。
2、考试地点选择:因为学校可能就我自己考PAT (才怪) ,所以选择在寝室阳台,因为下午较热,在前面挡了一个床单遮挡阳光,但是万万没想到在第二机位的手机因为同时进行者摄像头、热点(寝室网络可能不稳定)、充电(手机电池缩水严重)的缘故,导致手机高温过热保护,手机监考小程序客户端无法打开,浪费了一些时间。
3、手机正常后,因为天气加上手机故障的原因,其实心态变得开始浮躁、烦闷起来,阅读题目经常读几遍,这也是最后两题未做出来原因八 就是太菜,啥也不是 。hhh~

By The Way:我的经历希望以后有线上考试的同学们,多多检查设备还有环境,望大家取得好成绩。 upupup~~~


7-1 好数 (15 分)

好数是指由一对正整数 a<b 按照 a 2 a ^ 2 a2 + ab + b 2 b ^ 2 b2这个规则生成的数,a 和 b 就称为这个好数的源头。例如 91 就是一个好数,
因为 5 2 5 ^ 2 52 + 5 × 6 + 6 2 6 ^ 2 62 = 91,于是数对(5,6)就是 91 的源头。而对于一个好数,其源头并不一定唯一,例如(1,9)就是 91 的另一个源头。

本题就要求你编写程序,判断一个给定的数字是否好,并且输出好数的所有源头。

输入格式:
输入在第一行给出一个不超过 100 的正整数 N,随后 N 行,每行给出一个不超过 1 0 4 10 ^ 4 104的正整数。

输出格式:
对于每一个输入的数字,如果其是好数,则首先在一行中输出 Yes,然后每行输出它的一个源头,格式为 a b,按 a 的递增顺序输出;否则在一行中输出 No和比该数大的最小的好数,其间以空格分隔,然后每行输出这个好数的一个源头,格式同上。

输入样例
3
1
91
50

输出样例
No 7
1 2
Yes
1 9
5 6
No 52
2 6

解题思路

1、首先观察数据范围每个N都在 1 0 4 10^4 104以内,大致可以算一下对于最大的N假设它的源头是(i,j)均在60左右,所以用一个哈希表记录每个N可行的源头unordered_map<int, vector<PII>>,这样在处理每一个询问时可以直接查表输出N的所有源头,同时因为预处理时是按照从小到大的顺序的,所有哈希表李的每个源头都是按题目要求排列好的。

2、若询问未在预处理的好数里,要返回离他最近的一个好数并输出其结果,可以用一个C++里的有序集合set ,使用二分函数upper_bound可以快速找到set中比询问大的第一个数。

C++代码

#include<iostream>
#include<vector>
#include<unordered_map>
#include<set>

using namespace std;

typedef pair<int, int> PII;

int main(){

	unordered_map<int, vector<PII>> hash;
	set<int> S;
	for (int i = 1; i <= 100; i++){
		for (int j = i + 1; j <= 100; j++){
			int num = i * i + i * j + j * j;
			S.insert(num);
			hash[num].push_back({ i, j });
		}
	}
	int n; cin >> n;

	for (int i = 0; i < n; i++){
		int question; cin >> question;
		if (!S.count(question)){
			question = *S.upper_bound(question);
			cout << "No" << ' ' << question << endl;
		}else cout << "Yes" << endl;
		for (auto& p : hash[question]){
			cout << p.first << ' ' << p.second << endl;
		}
	}
	return 0;
}

7-2 数以类聚 (20 分)

我们把所有各位数字的乘积相同的数归为一类。例如 1362 和 2332 就是同一类,因为 1×3×6×2=2×3×3×2。给定 N 个正整数,请你判断它们可以被归成多少不同的类?

输入格式:
输入在第一行给出一个正整数 N(≤ 1 0 5 10 ^ 5 105),第二行给出 N 个不超过 1 0 7 10 ^ 7 107的正整数,数字间以空格分隔。

输出格式:
在一行中输出 N 个给定的整数可以归类的数量、以及规模最大的类中最小的乘积。数字间以一个空格分隔。

输入样例:
10
1234 98 329 9552 47621 8862 5539 2333 5365 463

输出样例:
7 54

样例说明:
10 个给定数字对应的各位数字乘积依次为:24、72、54、450、336、768、675、54、450、72。所以一共有 7 个不同的种类,即:24、72、54、450、336、768、675。
其中规模最大的有 2 个 54、2 个 72、2 个 450。即这 3 个种类都分别对应了 2 个数字,在规模相等、都是最大的情况下,我们输出最小的乘积 54。

解题思路

简单排序题:抠出数位->计算乘积->数据分组->数据排序->输出答案

C++代码

#include <iostream>
#include <unordered_map>
#include<set>
#include<vector>
#include<algorithm>

using namespace std;

unordered_map<int, int> mp;
typedef pair<int, int> PII;

struct mydata{
	int a, b;//数据, 记录数量
	bool operator< (const mydata& T)const {
		if (b != T.b) return b < T.b;
		else return a > T.a;
	}
};

void get_bits(long long n){
	int res = 1;
	while (n){
		res = res * (n % 10);
		n /= 10;
	}
	mp[res] += 1;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n; cin >> n;
	for (int i = 0; i < n; i++){
		long long num; cin >> num;
		get_bits(num);
	}
	vector<mydata> res;
	for (auto& v : mp){
		res.push_back({v.first, v.second});
	}
	sort(res.begin(), res.end());

	cout << mp.size() << ' ' << res.back().a << endl;
	return 0;
}

7-3 自定义判题程序 (20 分)

在每次允许插入、删除、修改一个字符的前提下,用最少的动作把一个字符串变成另一个字符串,是一道著名的可以用动态规划解决的问题。但判题的麻烦之处在于,虽然最小代价是唯一的,但变换方法却是不唯一的。例如把 PAT 变成 PTA 最少需要 2 步,可以保持第 1 个字母不变,修改后面 2 个字母,也可以保持第 1、2 个字母不变,在 A 前面插入 T,后面删除 T。由于拼题 A 系统的默认判题程序只能通过比对输出文件来判断对错,对这种正确答案输出不唯一的题目就不能处理了,需要出题者额外编写一个自定义判题程序来解决问题。

本题就请你编写这个自定义判题程序,读入两个字符串和用户程序产生的输出结果,判断他们的程序输出是否正确。

输入格式:
输入首先在前两行分别给出两个不超过 1000 个字符、以回车结束的非空字符串,第 1 行对应初始字符串,第 2 行对应目标字符串。

随后一行给出一个正整数 N(≤ 100),为需要判断的提交数。

接下来是 N 个提交的输出结果,每个结果占 2 行:第 1 行给出一个整数 K(不超出 32 位 int 的范围),为用户输出的动作数;第 2 行顺次描述对初始字符串的每个字符所做的操作:

如果这个字符不变,则在对应位置输出 0
如果这个字符被删除,则在对应位置输出 1
如果这个字符被改变,则在对应位置输出 2
如果这个字符前面或者后面插入了一个字符,则在插入的位置输出 3
注意我们要求用户提交的行首尾和数字间均无空格,所以如果有多余空格应判为错误。

题目保证这个操作序列不为空。

输出格式:
对每个正确的提交,在一行中输出 AC;否则输出 WA。

注意:这里并不要求你会用动态规划求出最优解。所以对“正确提交”的判断,并不以动态规划求出的最优解为根据! 对于用户输出的 K,如果其操作序列的确给出了 K 步操作并可以完成字符串的变换,则称为一个“可行解”。所谓“正确提交”,是指所有提交的可行解中的最优解。

输入样例:
This is a test.
Tom is a cat.
6
8
02330001100022100
8
11113330000001113300
6
022100000012200
5
033310000000200
6
0 2 2 1 000000 1 2 2 00
6
012200000022100

输出样例:
WA
WA
AC
WA
WA
AC


C++代码

参考这里C++代码

#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
int main()
{
	int t, k[11000];
	string a, b, s;
	int maxx = 9999999;
	getline(cin, a);
	getline(cin, b);
	cin >> t;
	for (int kk = 0; kk < t; kk++)
	{
		int n, j = 0, f = 0, cnt = 0;
		cin >> n;
		string c;
		getchar();
		getline(cin, s);
		for (int i = 0; i < s.size(); i++)
		{
			if (s[i] == '0')
			{
				c += a[j];
				j++;
			}
			else if (s[i] == '1')
			{
				j++;
				cnt++;
			}
			else if (s[i] == '2')
			{
				c += '^';
				j++;
				cnt++;
			}
			else if (s[i] == '3')
			{
				c += '^';
				cnt++;
			}
			else
			{
				f = 1;
			}
		}

		if (c.size() != b.size() || cnt != n)
			f = 1;

		for (int i = 0; i < b.size(); i++)
		{
			if (c[i] == b[i] || c[i] == '^') continue;
			else f = 1;
		}

		if (f == 0)
		{
			k[kk] = n;
			if (n < maxx) maxx = n;
		}
		else
		{
			k[kk] = -1;
		}
	}

	for (int i = 0; i < t; i++)
	{
		if (k[i] == maxx) cout << "AC" << endl;
		else cout << "WA" << endl;
	}

	system("pause");
	return 0;
}

7-4 数组与链表 (20 分)

让我们来设计这样一种数组与链表结合的整数存储的结构 A:
这种结构首先初始化一个长度为 L0
的整型数组 A0,返回给用户使用。当用户访问第 i 个元素 A[i] 的时候,如果 0 ≤ i < L0,则 A[i] 对应 A0[i],
系统就返回 h0​ + i × sizeof(int) 作为要访问的地址,其中 h0是数组 A0的起始位置,sizeof(int) 是数组元素的大小,这里元素就是 int 型,占 4 个字节。
当用户访问越界时(即 i ≥ L0),系统将另外开辟一个长度为 L1的数组 A1。此时 A[i] 对应 A1[j](这里 i 和 j 之间的映射关系留给你去推导)。如果 0≤j<L1,
则返回 h1+j × sizeof(int) 作为要访问的地址,其中 h1是数组 A1的起始位置。当 A1[j] 继续越界时,系统就按照上述规则继续开辟另一个长度为 L2​ 的数组 A2,并以此类推。

本题就请你实现这个系统功能,为用户的任何一次访问返回对应元素的地址。

输入格式:
输入第一行给出两个正整数 N(≤ 1 0 4 10 ^ 4 104)和 K(≤ $10 ^ 3%),分别为最多允许开出的数组个数、以及用户访问的次数。

此后 N 行,每行给出两个正整数,分别是一个数组的初始地址(≤ 1 0 7 10 ^ 7 107)和长度(≤ 100),其间以空格分隔。题目保证这些数组占据的空间无重叠。

最后一行顺序给出 K 个用户访问的数组下标,为区间 [0, 2 ^ 20] 内的整数。

输出格式:
对每个用户访问,在一行中输出对应的地址。但如果用户的访问超出了 N 个数组的存储范围,则这个访问是非法的,要输出 Illegal Access,并且对这个访问不采取任何处理。

最后一行请输出上述过程中一共创建了多少个数组。

输入样例:
6 7
2048 5
128 6
4016 10
1024 7
3072 12
9332 10
2 12 25 50 28 8 39

输出样例:
2056
4020
1040
Illegal Access
3072
140
3116
5


参考这里C++代码

C++代码

#include<iostream>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;
struct node
{
    int add;
    int size;
}t[100050];
vector<int>wz(10050000);

signed main()
{
    int n,k,all=0;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&t[i].add);
        scanf("%d",&t[i].size);
        all+=t[i].size;
        wz[i]=all-1;
    }
    all--;
    wz[0]=-1;
    int m,use=0,ans=0,ma=1;
    for(int i=1;i<=k;i++)
    {
        scanf("%d",&m);
        if(m>all)
        {
            printf("Illegal Access\n");
            continue;
        }
        int w;
        for(int i=1;i<=n;i++)
        {
            if(m<=wz[i])
            {
                w=i;
                break;
            }
        }
        ma=max(ma,w);
        printf("%d\n",t[w].add+(m-wz[w-1]-1)*4);
    }
    printf("%d",ma);
}

7-5 取帽子 (25 分)

拼题er们觉得戴帽子会令自己看上去很帅,所以他们不管到哪里都会戴着帽子。有一天他们去到一家餐厅,服务员把他们的帽子收集了堆起来保管。当大家要离开的时候,发现帽子被像上图那样摞起来了。于是你的任务就是帮他们排好队,使得每个人都能按顺序顺利取到自己的帽子。
已知每顶帽子的大小都不相同,并且帽子的尺寸跟帽子主人的体重有关 —— 越重的人戴的帽子就越大。

输入格式:
输入第一行给出一个正整数 N (≤ 1 0 4 10 ^ 4 104),为拼题er的人数。随后一行给出 N 个不同的帽子尺寸,为不超过 1 0 5 10 ^ 5 105 的正整数,顺序是从帽子堆的底部向上给出。
最后一行给出 N 个不同的体重,顺序对应编号从 1 到 N 的拼题er。
体重是不超过 1 0 6 10 ^ 6 106的正整数。一行中的数字以空格分隔。

输出格式:
在一行中按照取帽子的顺序输出帽子主人的编号。数字间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:
10
12 19 13 11 15 18 17 14 16 20
67 90 180 98 87 105 76 88 150 124

输出样例:
3 4 8 6 10 2 1 5 9 7
样例说明:
第一顶帽子的尺寸是最大的 20,所以对应第 3 个人的最大体重 180,于是第 3 个人排在最前面。
第二顶帽子的尺寸是第 6 小的 16,对应第 6 小的体重 98,是第 4 个人,于是第 4 个人下一个走。
以此类推。

解题思路

有一点拐弯的排序题。
1、对帽子尺寸大小和拼题er的重量排序。
2、逆序遍历帽子尺寸大小(帽子是底部向上给出的)。
3、对于每一个帽子尺寸,才排序的帽子数组中找到其在所有的帽子中排第几位C++ lower_bound)(记录 pos),二分查找效率快。
4、这个帽子位置pos即对应排序的拼题er数组的第pos位置的重量(记录 weight)。
5、根据weight,在原读入拼题er重量数组中找到这个位置对应的下标位置 + 1(拼题er编号从1开始,而数组编号从0开始),即为答案
(tips:因为原拼题er重量无序,只可以一次遍历查找;时间O(n))。

C++代码

#include<iostream>
#include<vector>
#include<unordered_map>
#include<algorithm>

using namespace std;

int main(){

	int n; cin >> n;
	vector<int> hat, sorthat;
	vector<int> wei, sortwei;
	for (int i = 0; i < n; i++){
		int hat_; cin >> hat_;
		hat.push_back(hat_);
		sorthat.push_back(hat_);
	}
	sort(sorthat.begin(), sorthat.end());

	for (int i = 0; i < n; i++){
		int wei_; cin >> wei_;
		wei.push_back(wei_);
		sortwei.push_back(wei_);
	}
	sort(sortwei.begin(), sortwei.end());

	for (int i = n - 1; i >= 0; i--){
		int pos = lower_bound(sorthat.begin(), sorthat.end(), hat[i]) - sorthat.begin();
		for (int j = 0; j < n; j++){
			if (wei[j] == sortwei[pos]){
				if(i == 0) cout << j + 1;
				else cout << j + 1 << ' ';
				break;
			}
		}
	}

	return 0;
}

好好吃饭,天天开心~

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值