2023秋季(模拟考试)

目录

前言:

A-1 Sequence of Grouped Numbers(分数 20)

题目:

思路:

代码:

A-2 Two Ways In, One Way Out(分数 25)

题目:

思路:

代码: 

问题2的拓展题目:

题目:

思路:

代码:

A-3 Popularity of Education Supermarket Categories(分数 25)

题目:

思路:

代码:

A-4 Auto Taxi(分数 30)

题目:

思路:

代码:

参考文章:


前言:

本套是我上次考过的一套,当时考的很差,这次模拟还可以,个人觉得这套的难度是大于2023夏季的。可以看到,第2题比较难,我也将重点讲解本题及其类型题的简单思路。

有时候考试没遇到自己倾向的题就很难受。

A-1 Sequence of Grouped Numbers(分数 20)

题目:

A sequence of grouped numbers is defined with the following pattern: the 1st group contains 1 number, which is 0×3+1; the 2nd group contains 2 numbers, which are 0×3+2 and 1×3+2; the 3rd group contains 3 numbers, which are 1×3+3, 2×3+3 and 3×3+3; and so on so forth. In general, the i-th group contains i numbers, which starts from the last number of the previous group plus 1, and is incremented by 3 thereafter for each number. For example, the 3rd group can be explained as the following: it contains 3 numbers, which starts from the last number of the 2nd group (that is 5) plus 1 (which gives 6), and then obtain 9 by incrementing 6 by 3, and finally get 12 by incrementing 9 by 3.
So by definition, the first 10 numbers of this sequence are: 1, 2, 5, 6, 9, 12, 13, 16, 19, and 22. They actually consists of 4 groups: {1}, {2, 5}, {6, 9, 12} and {13, 16, 19, 22}.
Your job is to tell the index of any given number in this sequence.

Input Specification:

Each input file contains one test case. Each case starts from a positive integer n (≤105) in a line, which is the number of queries. Then n lines follow, each contains a positive number that is no more than 105.

Output Specification:

For each queried number, output in a line its index in the sequence (starting from 1). If the number is not in, output Not Found instead.

Sample Input:

4
12
16
5941
87654

Sample Output:

6
8
2023
Not Found

思路:

第一题比较简单,你可以认为{1}, {2, 5}, {6, 9, 12} and {13, 16, 19, 22}分别是集合1,2,3,4,然后按照集合的个数按规律进行循环,相应的下标用数组存储。

代码:

#include<bits/stdc++.h>
using namespace std;
int N;
int a[100010];

int main()
{
	cin >> N;
	a[1] = 1;
	int cur = 2, last = 1;//下一个位置,上一个集合最后的一个值
	for (int i = 2;last <= 100000;i++)
	{
		int j = i;
		while (j && last<=100000)
		{
			if (j == i)
			{
				a[last + 1] = cur;
				last = last + 1;
			}
			else
			{
				a[last + 3] = cur;
				last = last + 3;
			}
			cur++, j--;
		}
	}
	while (N--)
	{
		int t; cin >> t;
		if (a[t]) cout << a[t] << endl;
		else cout << "Not Found" << endl;
	}
}

A-2 Two Ways In, One Way Out(分数 25)

题目:

Consider a special queue which is a linear structure that allows insertions at both ends, yet deletions at one end. Your job is to check, for a given insertion sequence, if a deletion sequence is possible. For example, if we insert 1, 2, 3, 4, and 5 in order, then it is possible to obtain 1, 3, 2, 5, and 4 as an output, but impossible to obtain 5, 1, 3, 2, and 4.

Input Specification:

Each input file contains one test case. For each case, the first line gives 2 positive integers N and K (≤10), which are the number of insertions and the number of queries, respectively. Then N distinct numbers (each no larger than 106) are given in the next line, as the insertion sequence. Finally K lines follow, each contains N inserted numbers as the deletion sequence to be checked.
All the numbers in a line are separated by spaces.

Output Specification:

For each deletion sequence, print in a line yes if it is indeed possible to be obtained, or no otherwise.

Sample Input:

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

Sample Output:

yes
no
yes
no

思路:

本题比较有难度,大概题意就是模拟一个可以两端输入,但只能一端输出的队列。

我们可以拿一个样例来仔细分析一下:

输入:10 2 3 4 5
输出:2 3 10 4 5


怎么考虑呢?

首先,我们可以指定左侧(前端)可以输出,两端都能输入,建立一个模拟队列q。

那么,我们不妨先尝试一下输入10,因为两端都可以输入,所以哪侧输入无所谓;

输入10之后呢?我们看一下输出,什么时候可以实现输出呢?只有当模拟队列q的首部等于输出序列的首部(会发生变化)时才能弹出,现在10\neq2,所以我们应该继续压入输入序列,这次压入的时候,因为队列不为空了,我们就应该考虑到从哪侧压入。

我们可以观察到2在输出序列中的位置是在10之前的,而且只能从一侧输出,因为我们只能采取将2从前端压入的方式;

接着我们队列的首部等于输出序列的首部了,那么队列要弹出队首元素,同时将输出序列队首移到3的位置。

然后重复上述步骤,直到输入序列结束为止。

那么,如何判定这个输出序列合法呢?很简单,我们可以指定一个游标tag,从0开始,要是最后tag=N,说明我们的输出序列能够和输入序列完美地匹配。


所以,按照上面的思路,我们需要设置一个数组ind用于判断输出序列的优先级,同时设置一个游标tag用于判定最终结果是否合法。

代码: 

#include<bits/stdc++.h>
using namespace std;
const int max_n = 1000010;
int a[max_n], b[max_n], ind[max_n], N, K;

bool check()
{
	deque<int> q;//默认前端出,两端进
	int tag = 0;
	for (int i = 0;i < N;i++)
	{
		if (q.empty()) q.push_back(a[i]);
		else
		{
			int k = q.front();
			if (ind[k] > ind[a[i]]) q.push_front(a[i]);//若出队列的顺序是在队首元素之前,则从前面插入
			else q.push_back(a[i]);//否则从后边插入
		}
		while (q.size() && q.front() == b[tag]) q.pop_front(), tag++;//什么时候可以出队呢?当模拟队列中队首元素等于未出队序列的首部时
	}
	return tag == N;
}

int main()
{
	cin >> N >> K;
	for (int i = 0;i < N;i++)
		cin >> a[i];
	for (int i = 0;i < K;i++)
	{
		for (int j = 0;j < N;j++)
		{
			cin >> b[j];
			ind[b[j]] = j;//表示出队列的先后顺序
		}
		if (check())
			cout << "yes" << endl;
		else
			cout << "no" << endl;
	}
}

问题2的拓展题目:

题目:

Consider a special queue which is a linear structure that allows insertions at one end, yet deletions at both ends. Your job is to check, for a given insertion sequence, if a deletion sequence is possible. For example, if we insert 1, 2, 3, 4, and 5 in order, then it is possible to obtain 1, 3, 2, 5, and 4 as an output, but impossible to obtain 5, 1, 3, 2, and 4.

Input Specification:
Each input file contains one test case. For each case, the first line gives 2 positive integers N and K (≤10), which are the number of insertions and the number of queries, respectively. Then N distinct numbers are given in the next line, as the insertion sequence. Finally K lines follow, each contains N inserted numbers as the deletion sequence to be checked.

All the numbers in a line are separated by spaces.

Output Specification:
For each deletion sequence, print in a line yes if it is indeed possible to be obtained, or no otherwise.

Sample Input:

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  

Sample Output:

yes
no
yes
yes

思路:

这个完全就是2的反问题,对一个队列指定了两端输出、一端输入。

我们仍然可以拿一个例子来讲解:

输入:10 2 3 4 5
输出:2 3 10 4 5


可以看到输入、输出仍和上面一样。如何考虑呢?

首先,我们指定只能从右侧(后端)输入,两侧输出。

我们不妨先压入10,可以看到此时输出序列的首部并没有包含在模拟队列中;

因此我们继续输入2,这一次我们可以发现输入序列的末尾和输出序列的首部相同,因此,将2从队列的后端弹出,输出序列的游标+1;

对于3也是同样的操作。

当我们来到10的时候,发现这时候输入序列的头尾和输出序列的首部相同,因此,将10从队列的前方弹出,输出序列的游标+1;

重复上述步骤,直到最后输入序列全部结束为止。
 

我们的判断条件还和之前用游标判断一致,这次我们的思想就是不断压入输入序列,当检测到队列的首尾与输出序列的首部相同时,就执行相关操作。

代码:

#include<bits/stdc++.h>
using namespace std;
const int max_n = 1000010;
int a[max_n], b[max_n], ind[max_n], N, K;

bool check()
{
	deque<int> q;//默认后端入、两端出
	int tag = 0;
	for (int i = 0;i<N;i++)
	{
		if (q.empty()) q.push_back(a[i]);
		else {
			if (q.front() != b[tag] && q.back() != b[tag])
				q.push_back(a[i]);
		}
		while (q.size() && (q.front() == b[tag] || q.back() == b[tag]))
		{
			if (q.front() == b[tag])
				q.pop_front(),tag++;
			else
				q.pop_back(),tag++;
		}
	}
	return tag == N;
}

int main()
{
	cin >> N >> K;
	for (int i = 0;i < N;i++)
		cin >> a[i];//入序列
	for (int i = 0;i < K;i++)
	{
		for (int j = 0;j < N;j++) cin >> b[j];
		if (check()) cout << "yes" << endl;
		else cout << "no" << endl;
	}
}

A-3 Popularity of Education Supermarket Categories(分数 25)

题目:

In PTA, there is the Education Supermarket which collects all kinds of exam/test problem sets. The sets can be found in several categories. The popularity of a category i is defined to be ni​/n+pi​/p, where ni​ and pi​ are the number of users who have opened a set and the total payment received in category i, respectively; n is the total number of users who have opened a set and p is the total payment received in the supermarket.
Your job is to calculate the popularity of any given category.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (≤103), which is the total number of problem sets and the categories. Then N lines follow, each describes a set or category in the following format:

ID1 ID2 type n p

where ID1 is the index of this set or category, which is a 3-digit number; ID2 is the index of the category that contains ID1 -- a category may have several sub-categories or contains several problem sets, and each set/category may only belong to one supper category; type being 1 means that ID1 is a category, or 0 means it is a problem set; if ID1 is a set, then n is the number of users who have opened it, and p is the total payment received for this set (both are non-negative integers no more than 105); or if ID1 is a category, there will be no n or p following type.
Note: the supermarket itself is the only root category, and its corresponding ID2 will be given as −1.
Finally, another positive integer M (≤103) is given, and is followed by M category ID's in the next line.

Output Specification:

For each query of category, we should print in a line its popularity. However, to avoid the lose of accuracy during float number calculations, let's output ni​, n, pi​ and p instead. It is guaranteed that none of n or p is zero.
Note: all the numbers in a line must be separated by exactly 1 space, and there must be no extra space at the beginning or the end of the line.

Sample Input:

13
001 -1 1
002 001 1
006 002 0 23 45
007 002 0 108 256
005 002 1
003 001 1
011 003 0 0 0
012 003 0 1 5
004 001 1
013 004 0 200 200
008 005 0 35 78
009 005 0 8 26
010 005 0 1130 1516
5
001 002 003 004 005

Sample Output:

1505 1505 2126 2126
1304 1505 1921 2126
1 1505 5 2126
200 1505 200 2126
1173 1505 1620 2126

思路:

这道题的题目就是一坨shit,又臭又长,简单来讲,其实就是有一颗树,孩子结点有两个属性值n,p,它的父亲结点的属性值等于所有孩子结点属性值之和。你需要做的就是求出除了孩子结点以外的所有结点的属性值,瞧瞧,这样说多直观。

其实就是一个dfs的问题,先求得靠近孩子结点的结点的属性值,然后一步步接近根节点。

代码:

#include<bits/stdc++.h>
using namespace std;
int N, K;

struct node {
	int sf;//0是叶子,1是中间,-1是源头
	int n = 0, p = 0;
}n[1000];
vector<int> v[1000];
int vis[1000];

pair<int,int> dfs(int cur)
{
	if (!n[cur].sf||vis[cur]) return make_pair(n[cur].n, n[cur].p);
	vis[cur] = 1;
	for (auto i : v[cur])
	{
		if (!vis[i])
		{
			auto k = dfs(i);
			n[cur].n += k.first, n[cur].p += k.second;
		}
	}
	return make_pair(n[cur].n, n[cur].p);
}

int main()
{
	cin >> N;
	int root;
	while (N--)
	{
		int a, b, c, d, e;
		cin >> a >> b;
		if (b == -1)
		{
			root = a;
			n[a].sf = -1;
			cin >> c;
			continue;
		}
		else {
			cin >> c;
			if (c == 1)
			{
				v[b].push_back(a);
				n[a].sf = 1;
			}
			else {
				v[b].push_back(a);
				cin >> d >> e;
				n[a].n = d;
				n[a].p = e;
			}
		}
	}
	dfs(root);
	cin >> K;
	while (K--)
	{
		int t; cin >> t;
		cout << n[t].n <<" "<< n[root].n <<" "<< n[t].p << " " << n[root].p << endl;
	}
}

A-4 Auto Taxi(分数 30)

题目:

You are supposed to implement an auto taxi management system: for each customer who is ordering a service, send a command to an auto taxi that can take the shortest time to reach the customer. The rules are:

  • One can only get a taxi and set one's destination at some pickup spots.
  • Any taxi can accept a command at any time after it shows up in the system.
  • Any taxi that is carrying passengers on the road must first send its passengers to their destination, before it can carry the next command;
  • If there is no more command, a taxi will stay at the destination of its last passenger and wait;
  • If there are many taxis that can reach the customer at the same shortest time, the command will be sent to the one without any passenger on board. If there is still a tie (that is, if all the taxis are carrying passengers, or several taxis are waiting), then pick the one with the smallest index, which is guaranteed to be unique.

Note: If a taxi arrives a destination at the same time as a customer submits an order, the taxi is considered empty, without any passenger on board.

Input Specification:

Each input file contains one test case. For each case, first a map is given in the following format:
In the first line, two positive integers are given: Nv​ (≤1000) and Ne​, which are the total number of pickup spots (hence the spots are indexed from 1 to Nv​) and the number of streets connecting them. Then Ne​ lines follow, each describes a street by

spot1 spot2 time

where spot1 and spot2 are the indices of the two ends of the street, and time is the positive integer time taken to pass this street, in minutes. It is assumed that no street will take more than 100 minutes to pass. And it is guaranteed that no duplicated information is given for any of the streets.
The next block is for the auto taxis.
In the first line, a positive integer M (≤1000) is given. Then M lines follow. The i-th line describes the status of the i-th taxi (hence the taxis are indexed from 1 to M) in the fomat:

pickup_time pickup_spot destination

means that the taxi has picked up someone from pickup_spot at pickup_time, and is driving to destination. Here pickup_time is in the form hh:mm, where hh is in [00, 23] and mm is in [00, 59]. When pickup_spot is the same as destination, it means that the taxi has no passenger and is waiting for the next command at destination.
The last block is for customer orders.
In the first line, a positive integer K (≤1000) is given. Then K lines follow, each describes an order in the fomat:

order_time pickup_spot destination

means that one has submitted an order of taxi at order_time (same format as pickup_time), going from pickup_spot to destination.
It is guaranteed that:

  • the orders are given in increasing order of order_time;
  • all the order_time's are after the latest pickup_time given in the taxis block, and are within the same day;
  • pickup_spot is never the same as destination;
  • all the pickup_spot's are connected to each other directly or indirectly; and
  • there is no one-way street road.

Output Specification:

For each order, output in a line the index of the taxi to be sent to the customer, following the rules given by the problem description.

Sample Input:

7 12
1 2 50
1 3 10
1 4 20
1 5 30
1 6 20
1 7 60
2 3 20
2 4 10
3 5 20
4 6 60
5 7 100
6 7 30
4
07:20 2 1
07:10 4 3
06:40 7 3
07:35 4 4
5
07:40 1 7
07:50 6 3
08:00 7 6
08:10 3 4
08:35 6 5

Sample Output:

2
1
2
3
1

思路:

最后一题还是挺难的,先要用n次堆优化dijkstra 求得两个站点之间的最短时间。接着对于每个出租车我们可以记录完成接到的所有订单后的时间,和完成订单后所在点的编号,这样对于每个顾客,我们可以遍历所有出租车,找到完成订单后到达顾客起点的最短时间,找到符合车辆即可。

代码:

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 1010;

int n, m;
vector<PII> eg[N];  // 存储邻接表,表示图的边
vector<int> dist[N];  // 存储最短距离数组

// Dijkstra算法的实现
vector<int> dijkstra(int S) {
	vector<int> dist(n + 1, -1);  // 初始化距离数组,初始值为-1
	vector<bool> st(n + 1, 0);  // 初始化状态数组,初始值为false
	priority_queue<PII, vector<PII>, greater<PII>> q;  // 定义一个优先队列,元素类型为PII,采用从小到大的顺序排列
	q.push({ 0, S });  // 将起点S加入队列,距离为0
	dist[S] = 0;  // 更新起点S的距离为0,自己到自己的距离为0
	while (q.size()) {
		auto t = q.top();  // 取出队列中距离最小的元素
		q.pop();
		if (st[t.second]) continue;  // 如果该节点已经被访问过,则跳过本次循环
		st[t.second] = 1;  // 将该节点标记为已访问
		for (auto [x, v] : eg[t.second]) {
			if (dist[x] == -1 or dist[x] > t.first + v) {
				dist[x] = t.first + v;  // 更新节点x的距离
				q.push({ dist[x], x });  // 将节点x加入队列
			}
		}
	}
	return dist;  // 返回最短距离数组
}

int get_time() {
	int h, m;
	char c;
	cin >> h >> c >> m;
	return h * 60 + m;
}

int idx[N];  // 存储每个查询的终点节点
int st[N], ed[N];  // 存储每个查询的起始时间和结束时间


int main() {
	cin >> n >> m;  // 输入节点数和边数
	for (int i = 1; i <= m; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		eg[a].pb({ b, c });  // 构建邻接表,表示图的边
		eg[b].pb({ a, c });
	}
	for (int i = 1; i <= n; i++) dist[i] = dijkstra(i);  // 使用Dijkstra算法计算每个节点到其他节点的最短距离
	cin >> m;  // 输入查询次数
	for (int i = 1; i <= m; i++) {
		int t = get_time();  // 获取查询的起始时间
		int l, r;
		cin >> l >> r;  // 输入查询的起点和终点
		st[i] = t, ed[i] = t + dist[l][r];  // 记录查询的起始时间和结束时间
		idx[i] = r;  // 记录查询的终点节点
	}

	int q;
	cin >> q;  // 输入额外的查询次数
	while (q--) {
		int t = get_time();  // 获取查询的起始时间
		int l, r;
		cin >> l >> r;  // 输入查询的起点和终点

		int res = -1, wt = 1e9;  // 初始化最优结果和最小等待时间

		for (int i = 1; i <= m; i++) {//从1开始遍历,保证了同等条件下选取下标最小的
			int d = max(t, ed[i]) - t + dist[idx[i]][l];  // 计算当前查询的等待时间
			if (d < wt) {  // 如果当前等待时间小于最小等待时间
				wt = d;  // 更新最小等待时间
				res = i;  // 更新最优结果
			}
			else if (d == wt && ed[i] <= t && ed[res] > t) {  // 如果当前等待时间等于最小等待时间且结束时间符合条件
				res = i;  // 更新最优结果
			}
		}

		ed[res] = wt + t + dist[l][r];  // 更新查询的结束时间
		idx[res] = r;  // 更新查询的终点节点

		cout << res << endl;  // 输出最优结果
	}
	return 0;
}

参考文章:

攀拓(PAT)- 程序设计(甲级)2023年秋季考试题解 - 知乎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值