PAT(甲级)2021年春季考试
7-1 Arithmetic Progression of Primes (20 分) 暴力搜索
【解题思路】
输入n的范围最大只有10,我们可以暴力搜索。
首先打表生成2到MAXP的所有素数。
最大的公差可能值为(MAXP-2)/(n-1),我们从大往小搜索,判断是否符合条件,如果符合则记录答案,结束循环。
特判,如果n=1,或者不存在符合条件的等差数列,直接输出小于MAXP的最大的素数。
注意,这里我们将素数存在数组当中。如果使用vector,样例4会段错误,不知道原因。
【满分代码】
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn=100005;
bool p[maxn];
int v[maxn];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,m,size=0;
cin>>n>>m;
for(int i=2;i<=m;i++)
{
if(!p[i])
{
v[size++]=i;
//vec.emplace_back(i);
for(int j=2*i;j<=m;j+=i)
p[j]=1;
}
}//素数打表
//cout<<size<<endl;
int dif=(m-2)/(n-1),begin,d,found=0;
if(n>1)
{
for(int i=dif;i>0;i--)
{
if(found) break;
for(int j=size-1;j>0;j--)
{
int cnt=1,next=v[j]-i;
while(p[next]==0&&next>=2)
{
cnt++;
next-=i;
}
if(cnt>=n)
{
d=i;
begin=v[j]-d*(n-1);
found=1;
break;
}
}
}
}
if(found)
{
cout<<begin;
for(int i=1;i<n;i++)
cout<<" "<<begin+i*d;
cout<<endl;
}
else
{
for(int i=m;i>=2;i--)
if(!p[i])
{
cout<<i<<endl;
break;
}
}
return 0;
}
7-2 Lab Access Scheduling (25 分) 区间贪心
【解题思路】
区间贪心模板题。
我们使用结构体存储每个人开始和结束时间,按照结束时间从早到晚排序。从前往后遍历一遍,如果时间不重叠,贪心选择结束时间最早的即可。
【满分代码】
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct node
{
int begin,end;
bool operator < (const node b) const
{
return end<b.end;
}
}p[2005];
int main()
{
int n,h,m,s,ans=1;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d:%d:%d",&h,&m,&s);
p[i].begin=h*3600+m*60+s;
scanf("%d:%d:%d",&h,&m,&s);
p[i].end=h*3600+m*60+s;
}
sort(p,p+n);
//for(int i=0;i<n;i++)
// cout<<p[i].end<<endl;
int sta=p[0].end;
for(int i=1;i<n;i++)
{
if(p[i].begin>=sta)
{
ans++;
sta=p[i].end;
}
}
cout<<ans<<endl;
return 0;
}
7-3 Structure of Max-Heap (25 分) 最大堆
【解题思路】
按照输入,将数字逐个加入最大堆中,并对堆进行调整。完成建堆以后,用一个map存储各数字在堆中的位置(数组下标),以供后面查询。
下面输入每行字符串,这里处理比较巧妙,逐个单词输入,并不需要用到复杂的字符串处理。具体看代码实现,每部分加上了注释,代表是五种查询中的哪一种,然后通过map找到数字对应的数组下标,判断是否符合,输出结果即可。
【满分代码】
#include <iostream>
#include <cstdio>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,m,a,b,d[1005];
string x,y,z,s,t;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>d[i];
for(int j=i;j>1;j/=2)
{
if(d[j]>d[j/2])
swap(d[j],d[j/2]);
else
break;
}
}//建最大堆
map<int,int> pos;
for(int i=1;i<=n;i++)
pos[d[i]]=i;//记录各数字在堆中的位置
while(m--)
{
cin>>a>>x;
if(x=="and")//a和b是否为兄弟结点
{
cin>>b>>y>>z;
if(pos[a]==0||pos[b]==0||pos[a]==pos[b])//结点a或b不存在,或者a和b是同一个结点
{
cout<<"0";
continue;
}
if(pos[a]/2==pos[b]/2)
cout<<"1";
else
cout<<"0";
}
else//(x=="is")
{
cin>>y>>z;
if(z=="root")//a是否为根节点
{
if(pos[a]==1)
cout<<"1";
else
cout<<"0";
}
else if(z=="parent")//a是否为b的父亲结点
{
cin>>s>>b;
if(pos[a]==0||pos[b]==0)
{
cout<<"0";
continue;
}
if(pos[a]==pos[b]/2)
cout<<"1";
else
cout<<"0";
}
else if(z=="left")//a是否为b的左子结点
{
cin>>s>>t>>b;
if(pos[a]==0||pos[b]==0)
{
cout<<"0";
continue;
}
if(pos[a]==pos[b]*2)
cout<<"1";
else
cout<<"0";
}
else if(z=="right")//a是否为b的右子结点
{
cin>>s>>t>>b;
if(pos[a]==0||pos[b]==0)
{
cout<<"0";
continue;
}
if(pos[a]==pos[b]*2+1)
cout<<"1";
else
cout<<"0";
}
}
}
return 0;
}
7-4 Recycling of Shared Bicycles (30 分) Floyd+DFS
【解题思路】
这道题是一个简单的图论问题,一共有从0到n编号的点,其中0是出发点。要求每次到达距离最短的下一个点(如果距离相等则编号小的点优先),最后输出访问路径。如果所有点都能访问到,输出路径长度之和;否则输出不能访问到的点。
n最大为200,数据范围不大,我们使用邻接矩阵存图。使用Floyd更新每两个点之间的最短路径长度。
从0结点出发,通过DFS记录每个点的访问情况和访问路径即可。
【满分代码】
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int INF=1000000000;
int n,m,G[205][205],vis[205],ans=0;
vector<int> vec;//存储访问路径
void dfs(int pos,int temp)
{
if(vec.size()==n)//1-n都已访问过
{
ans=temp;
return;
}
int minpos=0,mindis=INF,flag=0;
for(int i=0;i<=n;i++)
{
if(!vis[i])
if(G[pos][i]<mindis)
{
flag=1;
mindis=G[pos][i];
minpos=i;
}
}//找到最短距离的点(距离相同时编号小的点优先)
if(!flag) return;//没有点可以访问
vis[minpos]=1;
vec.emplace_back(minpos);
dfs(minpos,temp+mindis);//访问下一个点
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
int a,b,c;
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
{
if(i==j)
G[i][j]=0;
else
G[i][j]=INF;
}//邻接矩阵初始化
for(int i=0;i<m;i++)
{
cin>>a>>b>>c;
G[a][b]=G[b][a]=c;
}//输入图
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=n;k++)
if(G[i][k]+G[k][j]<G[i][j])
G[i][j]=G[i][k]+G[k][j];//Floyd最短路更新
vis[0]=1;
dfs(0,0);//从0点出发开始访问
cout<<"0";
for(int i=0;i<vec.size();i++)
cout<<" "<<vec[i];
cout<<endl;//输出路径
if(vec.size()==n)
cout<<ans<<endl;//如果所有点都访问过,输出路径长度之和
else
{
int flag=1;
for(int i=0;i<=n;i++)
if(!vis[i])
{
if(flag)
{
cout<<i;
flag=0;
}
else
cout<<" "<<i;
}
}//输出未访问到的点
return 0;
}