2020PAT春季

2020PAT线上赛

  • 模拟赛
    • Rational Arithmetic 20分
    • Insert or Merge 25分
    • Highest Price in Supply Chain 25分
    • All Roads Lead to Rome 30分
  • 正式赛
    • 7-1 Prime Day 20分
    • 7-2 The Judger 25分
    • 7-3 Safari Park 25分
    • 7-4 Replacement Selection 30分

因为疫情原因,春季的PAT考试变成线上考试了。

模拟赛

模拟赛的题目都选自以往真题,在官网真题练习中都能找到,有分数的加减乘除、插入和归并排序、树的BFS遍历以及求图的单源最短路径(除了距离最短还有其他标尺的那种),知识点考的真均匀。其中有三题都在书上看到过,但还是没拿到满分并且对着书也找不出自己写法错误的那种,害。看了一眼排名,前n页都是满分选手,我的83分是227名。

Rational Arithmetic 20分

就是分数的加减乘除,15分,有浮点错误。
原题链接:甲级 1088

#include // 15分,有浮点错误 
#include
using namespace std;
struct fraction{
	long long up;
	long long down;
};
long long gcd(long long a, long long b){
	if(b == 0) return a;
	else return gcd(b, a%b); 
}
fraction reduct(fraction a){
	// 对分数进行约分
	if(a.down < 0){
		a.down = abs(a.down);
		a.up = -a.up;
	}
	if(a.up == 0) a.down = 1; 
	long long d = gcd(abs(a.up), a.down);
	// 约分
	a.up /= d;
	a.down /= d;
	return a;
} 
void print(fraction a){
	// 输出一个分数
//	printf("print:%lld %lld\n", a.up, a.down);
	if(a.up < 0){
		printf("(-");
	}
	if(a.down == 1)
		printf("%lld", abs(a.up));
	else if(abs(a.up) > a.down){
		printf("%lld %lld/%lld", abs(a.up)/a.down, abs(a.up)%a.down, a.down);
	}
	else{
		printf("%lld/%lld", abs(a.up), a.down);
	}
	if(a.up < 0){
		printf(")");
	} 
} 
fraction add(fraction a, fraction b){
	fraction ans;
	ans.down = a.down*b.down;
	ans.up = a.up*b.down + b.up*a.down;
	ans = reduct(ans);
	return ans;
}
fraction minu(fraction a, fraction b){
	fraction ans;
	ans.down = a.down*b.down;
	ans.up = a.up*b.down - b.up*a.down;
	ans = reduct(ans);
	return ans;
}
fraction mul(fraction a, fraction b){
	fraction ans;
	ans.down = a.down*b.down;
	ans.up = a.up*b.up;
	ans = reduct(ans);
	return ans;
}
fraction de(fraction a, fraction b){
	fraction ans;
	ans.down = a.down*b.up;
	ans.up = a.up*b.down;
	ans = reduct(ans);
	return ans;
}
int main(){
	fraction a, b;
	scanf("%lld/%lld %lld/%lld", &a.up, &a.down, &b.up, &b.down);
	a = reduct(a);
	b = reduct(b);
	fraction temp;
	temp = add(a, b);
	print(a);
	printf(" + ");
	print(b);
	printf(" = ");
	print(temp);
	printf("\n");
	
	temp = minu(a, b);
	print(a);
	printf(" - ");
	print(b);
	printf(" = ");
	print(temp);
	printf("\n");
	
	temp = mul(a, b);
	print(a);
	printf(" * ");
	print(b);
	printf(" = ");
	print(temp);
	printf("\n");
	
	temp = de(a, b);
	print(a);
	printf(" / ");
	print(b);
	printf(" = ");
	print(temp);
	printf("\n");
	return 0; 
}

Insert or Merge 25分

考排序原理,时间也够,根据原理可以用sort偷懒,17分,也有错误,感觉是Merge那里的步长问题。
原题链接:甲级 1089

#include // 17分 
#include 
#include 
using namespace std;
vector<int> initial, result, temp1, temp2;
int n;
bool isInsert(){
	bool flag = false;
	for(int i=1; i<=n; i++){
		sort(temp1.begin(), temp1.begin()+i);
		if(temp1 == result){
			flag = true;
			printf("Insertion Sort\n");
			sort(temp1.begin(), temp1.begin()+i+1);
			for(int i=0; i<n; i++){
				printf("%d", temp1[i]);
				if(i != n-1)
					printf(" ");
				else
					printf("\n");
			}
			break;
		}
	}
	return flag;
}
void isMerge(){
	int i;
	for(i=1; i<=n; i*=2){
		for(int j=i; j<=n; j+=i){
			sort(temp2.begin()+j-i, temp2.begin()+j);
		}
		if(temp2 == result){
			i*=2;
			if(i <= n){
				for(int j=i; j<=n; j+=i){
					sort(temp2.begin()+j-i, temp2.begin()+j);
				}
			}
			else{
				sort(temp2.begin(), temp2.end());
			}
			for(int i=0; i<n; i++){
				printf("%d", temp2[i]);
				if(i != n-1)
					printf(" ");
				else
					printf("\n");
			}
		}
	}
}
int main(){
	scanf("%d", &n);
	int x;
	for(int i=0; i<n; i++){
		scanf("%d", &x);
		initial.push_back(x);
		temp1.push_back(x);
		temp2.push_back(x);
	}
	for(int i=0; i<n; i++){
		scanf("%d", &x);
		result.push_back(x);
		
	}
	bool flag;
	flag = isInsert();
	if(!flag){
		// 不是插入排序,是合并排序 
		printf("Merge Sort\n");
		isMerge();
	} 
	return 0;
} 

Highest Price in Supply Chain 25分

对树进行BFS,我爱BFS!
原题链接:甲级 1090

#include
#include
#include
using namespace std;
const int maxn = 100010;
struct node{
	int id;
	int height;
}root, temp;
vector<node> adj[maxn]; //存放每个结点的孩子 
int n;
int maxHeight=-1, height, num = 0;
bool isQueue[maxn] = {false};
void BFS(node root){
	queue<node> q;
	q.push(root);
	isQueue[root.id] = true;
	while(!q.empty()){
		node top = q.front();
		q.pop(); //不要忘记弹出去 
		height = top.height;
		if(height > maxHeight){
			maxHeight = height;
			num = 1;
		}
		else if(height == maxHeight){
			num++;
		}
		for(int i=0; i<adj[top.id].size(); i++){
			node child = adj[top.id][i];
			if(!isQueue[child.id]){
				child.height = top.height+1;
				q.push(child);
				isQueue[child.id] = true;
			}
		}
	}
}
int main(){
	scanf("%d", &n);
	double p, r;
	scanf("%lf%lf", &p, &r);
//	printf("p:%.2f r:%.2f\n", p, r);
	int father;
	for(int i=0; i<n; i++){
		//每个结点的双亲
		scanf("%d", &father);
		if(father == -1){
			root.id = i; //根结点的编号 
			root.height = 0;
		} 
		else{
			temp.id = i;
			adj[father].push_back(temp); // 注意关系 
		}
	}
	BFS(root);
//	printf("%d %d\n", maxHeight, num);
	r /= 100;
	double ans = p;
	for(int i=0; i<maxHeight; i++){
		ans *= (1+r);
	}
	printf("%.2f %d", ans, num);
	return 0;
}

All Roads Lead to Rome 30分

注意四个要求的分别是满足什么条件的路径,最后找路径是通过DFS的,这题超时了拿了26分。
原题链接:甲级1087

#include
#include
#include
#include
using namespace std;
const int INF = 0x3fffffff;
const int maxn = 210;
map<int, string> its;
map<string, int> sti;
int n, m;
int matrix[maxn][maxn]; //存放边权 
int w[maxn], weight[maxn]; //存放点权, 到每个点的最大点权 
int d[maxn]; //存放起点到其他点的最短距离
bool vis[maxn] = {false}; //存放点是否加入集合信息 
int num_route[maxn]; 
int goal; // 终点编号 
void Dijkstra(int s){
	fill(d, d+maxn, INF);
	d[s] = 0; // 初始时只有起点在集合中
	weight[s] = 0; 
	num_route[s] = 1; // 假设路线都只有一条 
	// 循环n遍
	for(int i=0; i<n; i++){
		// 找到d值最小的没有访问过的结点u
		int u=-1, min = INF;
		for(int i=0; i<n; i++){
			if(!vis[i] && d[i] < min){
				u = i;
				min = d[i];
			}
		} 
		if(u == -1) return ; // 不连通
		// 把该结点加入集合
		vis[u] = true;
		// 以该结点为中介优化与u相连的点到起点的最短距离
		for(int i=0; i<n; i++){
			if(!vis[i] && matrix[u][i]!=INF){
				if(d[i] > d[u]+matrix[u][i]){
					d[i] = d[u]+matrix[u][i]; //距离更新 
					weight[i] = weight[u]+w[i]; // 幸福值更新
					num_route[i] = num_route[u]; //路线条数继承 
				}
				else if(d[i] == d[u]+matrix[u][i]){
					num_route[i] += num_route[u];  //路线累加  
					if(weight[i] < weight[u]+w[i]){
						weight[i] = weight[u]+w[i]; // 距离一样选择点权更大的一条 
					}
				} 
			}
		} 
	} 
}
int minHeight = INF;
vector<int> temp, ans;
// 找到符合条件的那条路径 
void DFS(int s, int height, int happy, int cost){
	if(happy > weight[goal] | cost > d[goal]) return ;  
	if(s == goal){
		if(height < minHeight && happy == weight[goal] && cost == d[goal]){
			minHeight = height ;
			ans = temp;
		}			
	} 
	for(int i=0; i<n; i++){
		if(matrix[s][i] != INF){
			temp.push_back(i);
			DFS(i, height+1, happy+w[i], cost+matrix[s][i]);
			temp.pop_back();
		}
	}
} 
int main(){
	string name;
	cin>>n>>m>>name;
	int id = 0; //城市点编号从0开始到n-1 
	its.insert(make_pair(id, name));
	sti.insert(make_pair(name, id));
	id++;
	w[0] = 0; 
	for(int i=1; i<n; i++){
		cin>>name>>w[i];
		its.insert(make_pair(id, name));
		sti.insert(make_pair(name, id));
		if(name == "ROM"){
			goal = id; //记录罗马的编号 
		} 
		id++; 
	}
	string c1, c2;
	int dis;
	int id1, id2;
	// 先把边权矩阵初始化为无穷大
	fill(matrix[0], matrix[0]+maxn*maxn, INF); 
	for(int i=0; i<m; i++){
		cin>>c1>>c2>>dis;
		id1 = sti[c1];
		id2 = sti[c2];
		// 注意是无向边 
		matrix[id1][id2] = dis;
		matrix[id2][id1] = dis;
	}
	Dijkstra(0); // 求单源最短路径 
	
	temp.push_back(0); //源点肯定要加入路线的 
	DFS(0, 0, 0, 0);
	cout << num_route[goal] <<" " << d[goal] <<" "<< weight[goal] <<" "<< weight[goal]/minHeight << endl; 
	for(int i=0; i<ans.size(); i++){
		cout << its[ans[i]];
		if(i != ans.size()-1){
			cout << "->";
		}
	}
	return 0;
}

正式赛

最后拿了93分,出了一个样例点超时外,其他的答案错误不知道原因,大概172名+,我也想要王者徽章555

7-1 Prime Day 20分

这是一题字符串处理+判断素数的题目。本来想用筛法,但是空间不够,后来发现直接判断也没关系,这题可以拿到满分。

#include
#include
#include
#include
using namespace std;
//const int maxn = 100000010;
//bool isPrime[maxn] = {false};
vector<int> nums;
//void init(){
//	// 筛法求素数
//	fill(isPrime, isPrime+maxn, true);
//	isPrime[0] = false;
//	isPrime[1] = false;
//	for(int i=2; i
//		if(isPrime[i]){
//			for(int j=i*2; j
//				isPrime[j] = false;
//			}
//		} 
//	} 
//}
bool isPrime(int x){
//	bool flag = true;
	if(x == 1 || x == 0) return false;
	int xx = sqrt(x);
	for(int i=2; i<=xx; i++){
		if(x%i == 0){
			return false;
		}
	}
	return true;
}
int main(){
//	init(); 
	string str;
	cin>>str;
	int num = 0, len = str.length();
	for(int i=len-1; i>=0; i--){
		num = num + (str[i]-'0')*pow(10, len-i-1);
		nums.push_back(num);
	}
	int j=0;
	bool flag = true;
	for(int i=nums.size()-1; i>=0; i--){
//		cout << nums[i] << endl;
		cout << str.substr(j++) << " ";
		if(isPrime(nums[i])){
			cout << "Yes" << endl;
		}
		else{
			cout << "No" << endl;
			flag = false;
		}
	}
	if(flag){
		cout << "All Prime!";
	}
	return 0;
} 

7-2 The Judger 25分

这是一道模拟题,只拿到了20分,看没过的样例点是答案错误,直接模拟不会有超时的问题,答案错误真的很难找到错误在哪啊。

初始时给出两个数,然后不断地给出一个数,这个数不能和前面给出地数重复,并且必须是前面任意两个数之差,否则就失败。

#include// 20分 
#include
#include
#include 
using namespace std;
const int maxn = 100010;
bool isOK[maxn] = {false};
set<int> fail_set; // 失败数字集合 
vector<int> fail; // 出现了会失败的数字 
bool kickout[15] = {false}; // 淘汰玩家编号 
int nums[1010][15]; // 每一轮各个玩家出的数字 
int n, m; //玩家数,轮数 
void solve(){
	int x, y;
	for(int i=1; i<=m; i++){
		// 先检查一下是否所有玩家都淘汰
		bool flag = true; 
		for(int j=1; j<=n; j++){
			if(!kickout[j]) flag = false;
		}
		if(flag) break; // 都淘汰了,游戏结束 
		for(int j=1; j<=n; j++){
			if(!kickout[j]){
				// 只考虑没淘汰玩家的数字 
				x = nums[i][j]; //j号玩家在第i轮给出的数为x
				if(!isOK[x]){
					kickout[j] = true; // j号玩家淘汰 
					printf("Round #%d: %d is out.\n", i, j);
				}
				else{
					isOK[x] = false; // 已经出现过了,加入不可以数字集合 
					fail_set.insert(x);
					// 将该值和所有出现过的值做差,设为下一个玩家可以出的数字
					for(int i=0; i<fail.size(); i++){
						y = abs(x-fail[i]);
						if(fail_set.find(y) == fail_set.end()){
							// 不是重复的不可以出现的数字
							isOK[y] = true; 
						}
					}
					fail.push_back(x); 
				}
			} 
		}
	}
}
vector<int> winners;
int main(){
	int a, b; // 初始数字 
	scanf("%d%d", &a, &b);
	fail_set.insert(a); 
	fail_set.insert(b);
	fail.push_back(a);
	fail.push_back(b);
	isOK[abs(a-b)] = true;
	scanf("%d%d", &n, &m);
	int x;
	for(int i=1; i<=n; i++){
		for(int j=1; j<=m; j++){
			scanf("%d", &nums[j][i]);
		}
	}
	solve();
	for(int i=1; i<=n; i++){
		if(!kickout[i])
			winners.push_back(i);
	}
	if(winners.size() != 0){
		printf("Winner(s): ");
		for(int i=0; i<winners.size(); i++){
			printf("%d", winners[i]);
			if(i != winners.size()-1)
				printf(" ");
		}
	}
	else{
		printf("No winner.");
	}
	return 0;
} 
/*
101 42
4 5
49 17 67 9 7
17 9 8 50 7
25 92 43 26 37
59 17 1 41 40
*/ 

7-3 Safari Park 25分

这是一道图的搜索问题,几乎是图着色问题,因为地图着色是NPC问题(找不到多项式时间求解问题的算法,但是给定一个该问题的解,可以在多项式时间内验证该解的正确性,即NP Complete问题),我选择对图进行BFS,发现相邻点的颜色(这题是动物种类)相同,就返回false。

#include // 23分 
#include
#include
#include
using namespace std;
const int maxn = 510;
struct node{
	int species; // 该区域动物编号 
	vector<int> adj; // 邻接矩阵 
}Node[maxn];
int n, r, k;
set<int> st; 
bool inq[maxn] = {false};
bool BFS(int s){
	fill(inq, inq+maxn, false);
	queue<int> q;
	q.push(s);
	inq[s] = true;
	while(!q.empty()){
		int top = q.front();
		int species = Node[top].species;
		q.pop();
		for(int i=0; i<Node[top].adj.size(); i++){
			int v = Node[top].adj[i];
			if(Node[v].species == species){// 写外面对了 
				return false;
			}
			if(inq[v] == false){
//				if(Node[v].species == species){
//					return false;
//				}
				inq[v] = true;
				q.push(v);	
			}
		}
	}
	return true;
}
int main(){
	scanf("%d%d%d", &n, &r, &k);
	int a, b;
	for(int i=0; i<r; i++){
		// 无向边 
		scanf("%d%d", &a, &b);
		Node[a].adj.push_back(b);
		Node[b].adj.push_back(a);	
	}
	int m, x;
	scanf("%d", &m);
	for(int i=0; i<m; i++){
		st.clear();
//		for(int j=0; j
		for(int j=1; j<=n; j++){
			scanf("%d", &x);
			Node[j].species = x;
			st.insert(x);
		}
		if(st.size() < k){
			printf("Error: Too few species.\n");
		}
		else if(st.size() > k){
			printf("Error: Too many species.\n");
		}
		else{
			// BFS
			bool flag = BFS(1); // 编号从1开始 
			if(flag){
				printf("Yes\n") ;
			}
			else{
				printf("No\n");
			}
		}
	} 
}
/*
6 0 3
5
1 2 3 3 1 2
1 2 3 4 5 6
4 5 6 6 4 5
2 3 4 2 3 4
2 2 2 2 2 2
*/

7-4 Replacement Selection 30分

这是一道排序思想题,我之前没有接触过。但是感觉也是模拟题。

因为内存不够,只能放下m个数,但是有n个数需要进行排序。

这题可以用优先队列很好的解决,队列中的存放结构体,结构体中不光有该数的值,还存该数属于第几轮。那么先按照轮数小的排序,再按照值小的排序。

#include
#include
#include
using namespace std;
const int maxn = 100010; //元素数
struct node{
	int runs; //第几轮
	int value; //元素值 
}temp;
struct cmp{
	bool operator()(const node& a, const node&b){
		if(a.runs != b.runs){
			return a.runs > b.runs;
		}
		else{
			return a.value > b.value;
		}
	} 
};
priority_queue<node, vector<node>, cmp> pq; 
vector<int> result[maxn]; // 每一轮的元素数 
vector<int> nums;
int n, m; // 元素个数,内存容量 
void solve(){
	int i=0;
	while(pq.size() < m){
		temp.runs = 1;
		temp.value = nums[i++];
		pq.push(temp);
	}
	for(; i<n; i++){
		int data = nums[i];
		// 满了,取头元素出去
		node top = pq.top();
		pq.pop(); 
		result[top.runs].push_back(top.value);
		temp.value = data;
		if(data < top.value){
			temp.runs = top.runs+1;
		}
		else{
			temp.runs = top.runs;
		}
		pq.push(temp);
	}
	while(!pq.empty()){
		node top = pq.top();
		pq.pop(); 
		result[top.runs].push_back(top.value);
	}
}
int main(){
	scanf("%d%d", &n, &m);
	int x;
	for(int i=0; i<n; i++){
		scanf("%d", &x);
		nums.push_back(x);
	}
	solve();
	for(int i=1; i<maxn; i++){
		if(result[i].size() == 0) break; 
		for(int j=0; j<result[i].size(); j++){
			printf("%d", result[i][j]);
			if(j != result[i].size()-1)
				printf(" ");
			else
				printf("\n") ;
		}
	}
	return 0;
}

你可能感兴趣的:(杂记)