攀拓(PAT)- 程序设计(乙级)2023年夏季考试

目录

B-1 唯手熟尔 字符串处理

B-2 构造性证明 暴力

B-3 大数A+B 进制求和

B-4 陷阱

B-5 用两个栈实现队列  队列模拟

B-1 唯手熟尔 字符串处理

在“一年一度喜剧大赛”上有一部作品《少爷和我》,讲的是霸道管家龙傲天和憨厚少爷刘波的故事。管家为了保护少爷出去打了一架,回来轻描淡写地说“他们只有两百个人而已”…… 少爷问他是怎么解决的,他说:“无他,唯手熟尔。”
本题给出龙管家的日常起息录,其中打架用 fight 表示。你的任务就是统计一下,龙管家这几天到底打架多少次,才做到了“唯手熟尔”。

输入格式:

输入给出若干行由英文小写字母、空格、回车组成的文字,以一个特殊的符号 # 结束。题目保证输入不超过105 个字符。

输出格式:

在一行中输出 fight 出现的次数。

输入样例:

sleep eat fight fightfightfffighht fi
ght eat fight sleep fighting#

输出样例:

5

注意fighht 和 fi(回车)ght 都不算数。

#include<bits/stdc++.h>
using namespace std;
int main() {
	string s;
	int c = 0;
	while (cin >> s) {
		if (s.length() > 4) {
			for (int a = 0; a + 4 < s.length();) {
				if (s[a] == 'f' && s[a + 1] == 'i' && s[a + 2] == 'g' && s[a + 3] == 'h' && s[a + 4] == 't') {
					++c;
					a += 5;
				} else ++a;
			}
		}
		if (s[s.length() - 1] == '#')
			break;
	}
	printf("%d", c);
	return 0;
}

B-2 构造性证明 暴力

关于数学定理证明,也有高下之分。最暴力的证明方法是“构造性证明”,即当需要证明某种解存在时,直接把解构造出来,而不是仅通过推理证明解之存在。

下面有一个定理:

设 ai​(i=1,⋯,5)均为正实数。则一定存在 4 个互不相同的下标 i、j、k、l,使得 ∣ai​/aj​−ak​/al​∣<1/2。

作为程序员,就请你编写程序构造出正确的下标,验证这个结论。

输入格式:

输入在一行中顺序给出 5 个正实数。为保证计算中不产生浮点溢出,我们令输入的数字在 [10−10,1010] 区间内,且小数点后不超过 10 位小数。

输出格式:

在一行中首先输出使得定理结论成立的下标有多少套,随后输出最小的一套下标。数字间以 1 个空格分隔,行首尾不得有多余空格。
注:所谓下标集 i1​,⋯,i4​ 小于下标集 j1​,⋯,j4​,是指存在 1≤k≤4 使得 il​=jl​ 对所有 l<k 成立,且 ik​<jk​。

输入样例:

3.12 5.27 0.0007 9825.4413 10

输出样例:

18 1 4 3 2

样例解释:

易验证 ∣a1​/a4​−a3​/a2​∣=∣3.12/9825.4413−0.0007/5.27∣<1/2。满足条件的解有 18 个,例如 5、4、3、2 就是另一套解。

#include<bits/stdc++.h>
using namespace std;
int main() {
	long double r[5];
	for (int i = 0; i < 5; ++i) {
		scanf("%llf", &r[i]);
		getchar();
	}
	vector<vector<int>> t;
	for (int a = 0; a < 5; ++a)
		for (int b = 0; b < 5; ++b)
			for (int c = 0; c < 5; ++c)
				for (int d = 0; d < 5; ++d) {
					if (!(a == b || a == c || a == d || b == c || b == d || c == d)) {
						if (fabs(r[a] / r[b] - r[c] / r[d]) < 0.5) {
							vector<int> k;
							k.push_back(a + 1);
							k.push_back(b + 1);
							k.push_back(c + 1);
							k.push_back(d + 1);
							t.push_back(k);
						}
					}
				}
	if (t.size())	printf("%d %d %d %d %d", t.size(), t[0][0], t[0][1], t[0][2], t[0][3], t[0][4]);
	else printf("0");
	return 0;
}

B-3 大数A+B 进制求和

本题的任务很简单:给定两个正整数 A 和 B,请你输出 A+B 的值。麻烦在于,两个整数不一定是 10 进制的。

已知一个 d 进制的 k 位正整数 ak−1​⋯a1​a0​ 的十进制值为 ∑i=0k−1​ai​di,其中每一位数字 0≤ai​<d 且 ak−1​=0。

输入格式:

输入分两行,分别给出两个正整数 A 和 B。每行格式为:

k a[k-1] ... a[1] a[0]

对应 d 进制的 k 位正整数 a[k-1]...a[1]a[0],数字间以 1 个空格分隔。题目保证 k 为不超过 104 的正整数,且每位数字 a[i] 为不超过 103 的非负整数,最高位 a[k] 不为 0。

输出格式:

求最小可能的进制下 A+B 的值。输出格式与输入格式相同,行首尾不得有多余空格。

输入样例:

3 31 28 16
4 1 5 6 18

输出样例:

4 2 5 3 2

样例解释:

根据输入,这两个数的最小可能的进制是 32,因为有一位数字是 31。按 32 进制求和,得到一个 4 位的 32 进制数 2 5 3 2。

#include<bits/stdc++.h>
using namespace std;
int main() {
	int n1, n2, max = 0;
	cin >> n1;
	int a1[n1];
	for (int i = 0; i < n1; ++i) {
		cin >> a1[i];
		if (a1[i] > max) max = a1[i];
	}
	cin >> n2;
	int a2[n2];
	for (int i = 0; i < n2; ++i) {
		cin >> a2[i];
		if (a2[i] > max) max = a2[i];
	}
	++max;
	int min = n1 > n2 ? n2 : n1;
	reverse(a1, a1 + n1);
	reverse(a2, a2 + n2);
	int l = 0, i = 0;
	vector<int> v;
	for (i; i < min; ++i) {
		int res = a1[i] + a2[i] + l;
		v.push_back(res % max);
		l = res / max;
	}
	while (i < n1) {
		v.push_back((a1[i] + l) % max);
		l = (a1[i] + l) / max;
		++i;
	}
	while (i < n2) {
		v.push_back((a2[i] + l) % max);
		l = (a2[i] + l) / max;
		++i;
	}
	if (l) v.push_back(1);
	printf("%d", v.size());
	for (i = v.size() - 1; i > -1; --i)
		printf(" %d", v[i]);
	return 0;
}

B-4 陷阱

一种机器人的行为是这样设计的:它的目标是从一个方形地图的南边移动到北边。当中途遇到障碍物时,它只能向西转向,然后移动到可以向北的时候,再转向北继续。

给定一张由 n×n 个方格子组成的正方形地图,机器人可以从地图的最底边(南边)下面的位置出发,目标是到达地图的最上边(北边)。(顺便说:地图的左边是西,右边是东。)

如果图中放置了障碍物,机器人是有可能落入陷阱的,即从某些位置出发,会永远无法到达北边。例如在下图中,如果机器人从 7 号或者 8 号位置出发,就会落入陷阱。

map.jpg

本题就请你指出那些会令机器人落入陷阱的位置。

注意:假设机器人可以走出地图边界,而围绕地图边界的所有格子都是空的,没有障碍物。如果机器人从地图东、西边界外的任意起点出发,都可以毫无障碍地到达北边,所以我们显然只需要考虑东、西边界内的起始位置(例如上图南边下面的位置 1~10)。此外,只要能到达北边,哪怕不在地图范围内也算是成功的(例如上图从位置 1、2 出发就会走出地图,但仍然能到达北边)。

输入格式:

输入第一行首先给出一个正整数 n (≤100),是地图的规模。随后 n 行,每行给出 n 个字符,或者是 0 代表此方格为空,或者是 1 代表此方格有障碍物。第一行对应的是北边,最后一行对应的是南边。

输出格式:

在一行中输出所有会令机器人落入陷阱的起始位置,按升序输出。这些位置从西向东顺次编号,编号从 1 开始。题目保证至少有一个输出。
一行中所有数字以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

10
0000000000
0000111010
1100100011
0000110001
0000000011
0000000000
0100000100
0001000000
0001000000
0001100000

输出样例:

7 8

思路

        一旦移动到最左侧列或者最顶层则一定不会落入陷阱  因为题目说明了边界范围之外都是无阻碍的!

        所以第一个位置一定不会陷进去 所以遍历变量从1开始也就是从第二个位置开始遍历

        x y 表示当前位置上面的那个格子的坐标  先判断当前位置上面那个格子有无阻碍  无则x--即向上走一格     有阻碍则判断当前元素左侧是否有阻碍 也就是判断坐标为x+1 y-1的格子是否有阻碍

 若有则说明落入陷阱,break掉   若无则y-- 即向左移一格

#include<bits/stdc++.h>
using namespace std;
int main() {
	int n;
	scanf("%d", &n);
	getchar();
	char a[n][n];
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < n; ++j)
			scanf("%c", &a[i][j]);
		getchar();
	}
	bool f = 1;
	for (int i = 1; i < n; ++i) {
		int x = n - 1, y = i;
		bool o = 1;
		while (y != 0 && !(x == 0 && a[x][y] != '1')) {
			if (a[x][y] != '1') --x;
			else if (a[x + 1][y - 1] != '1') --y;
			else {
				o = 0;
				break;
			}
		}
		if (!o) {
			printf("%s%d", f ? "" : " " , i + 1);
			f = 0;
		}
	}
	return 0;
}

B-5 用两个栈实现队列  队列模拟

一个队列(先进先出结构)可以用两个堆栈(后进先出结构)来实现,方法如下:

  1. 从两个空堆栈 s1​ 和 s2​ 开始。
  2. 当元素 e 入队时,它实际上是被推入到 s1​。
  3. 当我们需要出队时,首先检查 s2​。如果 s2​ 是空的,则把 s1​ 中的元素全部导入 s2​,即将每个元素从 s1​ 弹出后马上推入 s2​。然后从 s2​ 中弹出元素 —— s2​ 顶端元素一定是第一个进入 s1​ 的,所以是应该出列的第一个元素。

假设每个堆栈的推入和弹出操作都用 1 个单位时间,请你给出每个出队操作所花的时间。

输入格式:

输入首先在一行中给出一个正整数 N(≤103),是操作数量。随后 N 行,每行按以下格式给出一个操作:

操作 元素

其中 操作 或者是 I 表示入队,或者是 O 表示出队。每个 I 后面跟的 元素 是一个不超过 106 的正整数。O 操作后面不跟任何元素。
题目保证至少有一个 O 操作。

输出格式:

对每个出队操作,在一行中输出出队的那个元素和这出队操作所花费的单位时间数量,其间以 1 个空格分隔,行首尾不得有多余空格。
若出队操作被调用时队列是空的,则在对应行中输出 ERROR

输入样例:

10
I 20
I 32
O
I 11
O
O
O
I 100
I 66
O

输出样例:

20 5
32 1
11 3
ERROR
100 5

思路

        开两个双端队列deque<int> d1 d2;

        入栈则入d1的back 出栈则检测d2是否为空 若为空则d1元素全部pop_back()即从back端出栈到d2   从d2的back端入栈 然后出栈一个元素  耗费的时间相当于  d2.size() * 2 + 1 移动了的元素的个数*2 再 加出栈一个元素耗费的1单位时间 总共 d2.size() * 2 + 1 . 若出栈时候d2有元素,则知直接出栈 耗费时间为1

        俩版本自己看

#include<bits/stdc++.h>
using namespace std;
int main() {
	int n, t;
	char a;
	scanf("%d", &n);
	getchar();
	deque<int> d1, d2;
	for (int i = 0; i < n; ++i) {
		scanf("%c", &a);
		getchar();
		if (a == 'I') {
			scanf("%d", &t);
			getchar();
			d1.push_back(t);
		} else if (a == 'O') {
			if (d1.empty()) printf("ERROR\n");
			else {
				if (d2.empty()) {
					while (!d1.empty()) {
						d2.push_back(d1.back());
						d1.pop_back();
					}
					printf("%d %d\n", d2.back(), d2.size() * 2 + 1);
				} else
					printf("%d %d\n", d2.back(), 1);
				d2.pop_back();
			}
		}
	}
	return 0;
}
#include<bits/stdc++.h>
using namespace std;
int main() {
	int n, t2;
	char t1;
	scanf("%d", &n);
	getchar();
	deque<int> d1, d2;
	for (int i = 0; i < n; ++i) {
		scanf("%c", &t1);
		getchar();
		if (t1 == 'I') {
			scanf("%d", &t2);
			getchar();
			d1.push_back(t2);
		} else {
			if (!d2.size() && !d1.size()) {
				printf("ERROR\n");
				continue;
			}
			int sum = 1;
			if (!d2.size())
				for (int j = d1.size() - 1; j > -1; --j) {
					d2.push_back(d1.back());
					d1.pop_back();
					sum += 2;
				}
			printf("%d %d\n", d2.back(), sum);
			d2.pop_back();
		}
	}
	return 0;
}

  • 24
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值