2021 春季PAT甲级满分题解

因为才知道2021年夏令营只能看本年的PAT成绩,所以冬季参加的就作废了。。
我的钱啊

没办法只能参加这次的PAT。

这次的PAT是真的惊险,中间差点就以为自己无了,能告诉你我距离考试结束还有1:30的时候只有42分吗。。还好自己告诉自己别慌,也是因为最后一道题比较容易打把。

首先第一题:
第一题的意思是给你一个n,m,让你找到最后一项在m里面的n项等差数列,这道题思维难度比较低,因为等差数列只要确定了相邻的两项就可以确定一个等差数列了,所以只要先预处理m以内的质数然后O(n^2)的枚举最后一项和倒数第二项,就可以得出之前的项,然后一一看是不是质数就可以了。

然后你发现你TLE了。。
这个题还需要一些剪枝(大佬或常数小的人无视)

我的剪枝是如果这个数的第一项<2和如果nowdist<dist就不用看了,也就是v[j]-dis*(n-1)<1||nowdis<dist continue;

然后就过了.(很惊险的,一个点505ms…)

Code:

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
ll gcd(ll a, ll b){
	if(a==0) return b;
	else return gcd(b%a,a);
}
//tree<ll,null_type,less<ll>,rb_tree_tag,tree_node_statis_update> tr;
int isprime(int x){
	if(x<2) return 0;
	for(int i=2;i*i<=x;i++){
		if(x%i==0) return 0;
	}
	return 1;
}
vector<int> v;
void prime(int n){
	for(int i=2;i<=n;i++){
		if(isprime(i)) v.push_back(i);
	}
	//sort(v.begin(),v.end());
}
vector<int> ans;
int main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	int n,m,dist=0,firnum=-1;
	cin>>n>>m;
	
	prime(m);//所有素数
	int tag=0;
	//for(int i=0;i<v.size();i++) cout<<v[i]<<' ';
	ans.resize(n+7);
	vector<int> v2;
	for(int i=0;i<v.size();i++){
		for(int j=i+1;j<v.size();j++){//一一枚举等差数列最后两项
			int nowdis = v[j]-v[i];//当前的dis
			if(nowdis<dist||v[j]-nowdis*(n-1)<2) continue;//剪枝
			int len=n-2, now=v[i]-nowdis;
			v2.clear();
			v2.push_back(v[j]), v2.push_back(v[i]);
			while(len>0){
				if(isprime(now)){
					v2.push_back(now), now-=nowdis, len--;//逐一向前
				} 
				else break;
			} 
			if(len==0&&(nowdis>dist||(nowdis==dist&&v2[v2.size()-1]>firnum))){//更新答案
				for(int i=v2.size()-1;i>=0;i--){
					ans[v2.size()-1-i]=v2[i];
				}
				firnum = ans[0], dist = nowdis;
				tag=1;
			}
		}
	}
	if(!tag) cout<<v[v.size()-1];
	else{
		for(int i=0;i<n;i++){
			cout<<ans[i];
			if(i!=n-1) cout<<' ';
		}
	}
}

我能说第二题是我最后A的吗。。

我一直22分,然后发现自己时间求错了,substr使用算错了,就这都有22…

这道题是一个字符串模拟,加上基础的贪心就ok了

这道题就是给你N个区间,然后每个点只能有一段区间,问最多能放多少区间?

就是贪心选择结束时间最早的区间就行了,注意这里区间端点重叠也ok

#include<bits/stdc++.h>
using namespace std;
const int N = 2e3+7;
int toint(string s){
	int x = 0;
	for(int i=0;i<s.size();i++){
		x = x*10+s[i]-'0';
	}
	return x;
}
int totime(string s){
	string h=s.substr(0,2), m=s.substr(3,2), ss=s.substr(6,2);//(就是这里算成s.substr(5,2)了。。)
	return toint(h)*3600+toint(m)*60+toint(ss);
}

struct line{
	int from,to;
}g[N];
int cmp(line x,line y){
	return x.to<y.to;
}
int main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	int n;
	cin>>n;
	string s,s2;
	for(int i=0;i<n;i++){
		cin>>s>>s2;
		g[i].from = totime(s), g[i].to = totime(s2);
		if(g[i].from>=g[i].to) swap(g[i].from,g[i].to);
	}
	int ans = 0;
	sort(g,g+n,cmp);
	int last = -1;
	for(int i=0;i<n;i++){
		if(i==0||g[i].from>=last){
			ans++;
			last = g[i].to;
		} 
	}
	if(n==2000){//这个是开始想骗分。。。,后来发现是时间求错了
		cout<<ans;
	}
	else cout<<ans;
	
}

第三题是让你模拟一个大顶堆(插入N个数字),然后有M个询问,问你这个询问对不对(都是什么x是不是y左儿子这样的)

这个题比较基础,但我一年没学基础数据结构,居然都把大顶堆忘记了,着急了5分钟,冷静的在纸面上写了写,就想起来了。。(花了我20分钟)

然后看了问题有点绝望(巨型的分类讨论+模拟啊),因为这个时候我离考试结束只有1:10了,只有42分。。。

但是也没有办法,喝了一大口水,然后开干,先用getline(cin,s),以空格为分界线,一点点扣字符串。

其实这道题难点主要是字符串模拟,大顶堆部分因为是一颗完全二叉树所以孩子就是x2+1和x2,父亲就是x/2,只要使用map模拟就行了,每次插入一点点向父亲更新就行,直到到根或者是满足大顶堆了。
复杂度O(n*lgn)(不过PAT一般不会TLE的)

然后两个map一个是key->val,一个是val->key
x,y兄弟->mp2[x]/2=mp2[y]/2
x根->mp[1]=x
x y父亲->mp2[y]/2=x
左右 -> mp[y]*2(+1)=x

就行了,注意数值不存在(直接输出0)和负数string转int

Code:

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
const int N = 1e3+7;
struct node{
	int val, key;
	int left, right, tag=1, p;
}g[N];
map<int,int> mp;
map<int,int> mp2;
int root=1;
int cnt=1, zpx, zpy;
int toint(string s){
	int x = 0, tag=1;
	int i;
	if(s[0]=='-') i=1, tag=-1;
	else i=0;
	for(;i<s.size();i++){
		x = x*10+s[i]-'0';
	}
	return x*tag;
}
int judge(string s){
	int n = s.size();
	int i;
	int last = 0, tag;
	for(i=0;i<n;i++){
		if(s[i]==' ') break;
	}
	zpx = toint(s.substr(0,i));
	i++;
	last = i;
	string s2,s3,s4;
	for(i;i<n;i++){
		if(s[i]==' ') break;
	}
	s2 = s.substr(last,i-last);
	i++;
	last = i;
	for(i;i<n;i++){
		if(s[i]==' ') break;
	}
	s3 = s.substr(last,i-last);
	if(s2=="and"){
		zpy = toint(s3);
		return 1;
	}
	i++;
	last = i;
	for(i;i<n;i++){
		if(s[i]==' ') break;
	}
	s4 = s.substr(last,i-last);
	if(s4=="root") return 0;
	else if(s4=="parent") tag=2;
	else if(s4=="left") tag=3;
	else tag=4;
	s4="";
	for(i=n-1;i>=0;i--){
		if(s[i]==' ') break;
	}
	s4 = s.substr(i+1,s.size()-i-1);
	//if(tag==2) cout<<'\n'<<s4<<'\n';
	zpy = toint(s4);
	return tag;
}
int main(){
	//std::ios::sync_with_stdio(false);
	//cin.tie(0);
	int n,m,x;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>x;
		mp[i]=x;
		int zp = i;
		while(zp/2>0&&mp[zp]>mp[zp/2]){
			swap(mp[zp],mp[zp/2]);
			zp/=2;
		}
	}
	for(int i=1;i<=n;i++){
		mp2[mp[i]] = i;
		//cout<<mp[i]<<' '<<i<<'\n';
	}
	string s;
	getline(cin,s);
	while(m--){
		getline(cin,s);
		//cout<<s;
		int tag = judge(s);
		if(tag>=1&&(!mp2.count(zpx)||!mp2.count(zpy))){
			cout<<0;
			continue;
		}
		if(tag==0){
			if(mp[1]==zpx) cout<<1;
			else cout<<0;
		}
		else if(tag==1){
			if(mp2[zpx]/2==mp2[zpy]/2) cout<<1;
			else cout<<0;
		}
		else if(tag==2){
			if(mp2[zpy]/2==mp2[zpx]) cout<<1;
			else cout<<0;
		}
		else if(tag==3){
			if(mp2[zpy]*2==mp2[zpx]) cout<<1;
			else cout<<0;
		}
		else{
			if(mp2[zpy]*2+1==mp2[zpx]) cout<<1;
			else cout<<0;
		}
	}
}

第四题
第四题是让你模拟一个车,这个车每次都贪心的从所有点里面选一个没访问过的距离最小的点(距离相同就看节点大小),让你依次输出他经过的节点和行驶总距离

看到题目的时候离考试结束还有 00:45,但当时没啥感觉。。可能真的是绝望了吧,就想做一下吧,做不出来就,也没想这个。。

第四题,我第一次读题以为是一步走到的点里面最近的呢,但是样例一就是推不出来,后来一遍遍读题枚举题意(主要是百思不得其解为什么N那么小),突然一想n^3好像有个floyd,才知道是全部的点。

那就是个非常简单的floyd+dfs了,每次贪心选择最小的点,如果没全部的点都vis过就第二种形式输出

结果WA了->22,为啥啊

原来没看见要是没全部的点,第一行是按照访问顺序的。。
原来。。

改一下就AC了,复杂度O(n^3)(还是PAT只要你复杂度别离谱基本都是A)

Code:

#include<bits/stdc++.h>
using namespace std;
const int N = 507;
typedef long long ll;
ll ans = 0, vis[N];
ll inf  = LONG_LONG_MAX;
vector<int> v2;
ll f[207][207];
int cnt = 0;
void dfs(int u,int n){
	if(!vis[u]) v2.push_back(u), cnt++;
	vis[u]=1;
	ll now,val=inf;
	for(int i=1;i<=n;i++){
		if(u==i||f[u][i]==0||vis[i]==1) continue;
		if(f[u][i]<val) now=i, val=f[u][i];
	}
	if(val!=inf&&cnt<=n+1) ans += val,dfs(now,n);
}
vector<int> v3,v4;
void floid(int n){
	for(int i=0;i<=n;i++){
		for(int j=0;j<=n;j++){
			for(int k=0;k<=n;k++){
				if(f[j][i]==0||f[i][k]==0) continue;
				if(f[j][k]==0) f[j][k] = f[j][i]+f[i][k];
				else f[j][k] = min(f[j][i]+f[i][k],f[j][k]);
			}
		}
	}
}
int main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	ll n,m,x,y,w;
	cin>>n>>m;
	for(int i=0;i<m;i++){
		cin>>x>>y>>w;
		if(f[x][y]==0){
			f[x][y]=w;
			f[y][x]=w;
		}
		else
		{
			f[x][y]=f[y][x]=min(w,f[x][y]);
		}
	}
	floid(n);
	
	dfs(0,n);
	for(int i=0;i<=n;i++){
		//if(vis[i]) v3.push_back(i);
		if(!vis[i])v4.push_back(i);
	}
	if(v4.size()!=0){
		
		for(int i=0;i<v2.size();i++){
			cout<<v2[i];		
			if(i!=v2.size()-1) cout<<' ';
			else cout<<'\n';
		}
		for(int i=0;i<v4.size();i++){
			cout<<v4[i];		
			if(i!=v4.size()-1) cout<<' ';
		}
	}
	else{
		//memset(vis,0,sizeof vis);
		for(int i=0;i<v2.size();i++){
			cout<<v2[i];
			if(i!=v2.size()-1) cout<<' ';
			else cout<<'\n';
		}
		cout<<ans;
	}

}

这里我的blog就写完了,写这篇blog主要是记录自己的心理,提醒自己无论什么时候别慌。。

后记:
感觉自己还是运气比较好的,因为我第三道题一下就A了,没有陷进去,我看到好多99/97都是第三道题有一个点,很可惜。

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值