PAT乙级考前总结(三)

Posted fremontxutheultimate

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PAT乙级考前总结(三)相关的知识,希望对你有一定的参考价值。

特殊题型1027 打印沙漏 (20 分)题略,感觉有点像大学里考试的题。找规律即可。

#include <stdio.h>#include <iostream>using namespace std;int main(){

  int n;

  char c;

  cin>>n>>c;

  int s=1,i=1;

  for(i=1;;i++)

  {

   s+=2*(2*i+1);

   if(s>n) break;

  }

  for(int j=i-1;j>=0;j--)

  {

   for(int l=1;l<=i-j-1;l++) cout<<‘ ‘;

   for(int l=1;l<=2*j+1;l++) cout<<c;

   cout<<endl;

  }

  for(int j=1;j<=i-1;j++)

  {

   for(int l=1;l<=i-j-1;l++) cout<<‘ ‘;

   for(int l=1;l<=2*j+1;l++) cout<<c;

   cout<<endl;

  }

  cout<<n-s+4*i+2;

  return 0;}1036 跟奥巴马一起编程 (15 分)四舍五入时要注意一下#include <stdio.h>#include <iostream>using namespace std;int main(){

  int n;

  char c;

  cin>>n>>c;

  int m=(double)n/2+0.5;

  for(int i=0;i<m;i++)

  {

   for(int j=0;j<n;j++)

   {

   if(i==0||i==m-1||j==0||j==n-1) cout<<c;

   else cout<<‘ ‘;  

}

cout<<endl;

  }

  return 0;}1020 月饼 (25 分)月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请你计算可以获得的最大收益是多少。注意:销售时允许取出一部分库存。样例给出的情形是这样的:假如我们有 3 种月饼,其库存量分别为 18、15、10 万吨,总售价分别为 75、72、45 亿元。如果市场的最大需求量只有 20 万吨,那么我们最大收益策略应该是卖出全部 15 万吨第 2 种月饼、以及 5 万吨第 3 种月饼,获得 72 + 45/2 = 94.5(亿元)。输入格式:每个输入包含一个测试用例。每个测试用例先给出一个不超过 1000 的正整数 N 表示月饼的种类数、以及不超过 500(以万吨为单位)的正整数 D 表示市场最大需求量。随后一行给出 N 个正数表示每种月饼的库存量(以万吨为单位);最后一行给出 N 个正数表示每种月饼的总售价(以亿元为单位)。数字间以空格分隔。输出格式:对每组测试用例,在一行中输出最大收益,以亿元为单位并精确到小数点后 2 位。乙级考数据结构和算法比较少,而这一题可以用到贪心,但其实没那么死板。#include <cstdio>#include <algorithm>using namespace std;struct mc//定义结构 {

double total;double price;}save[1010];bool cmp(mc a,mc b)//比较函数 {

return a.price>b.price;}int main(){

int n;double d,sum;

scanf("%d %lf",&n,&d);

for(int i=0;i<n;i++)

{

scanf("%lf",&save[i].total);

}

for(int i=0;i<n;i++)

{

scanf("%lf",&sum);

save[i].price=sum/save[i].total;//单价

}

sort(save,save+n,cmp);//排序 ,显然越贵的越赚钱

sum=0;

for(int i=0;i<n;i++)

{

if(d>=save[i].total)// 没有超出需求,可以全卖

{

sum+=save[i].price*save[i].total;

d-=save[i].total;//需求要更新

}else if(d>0)//卖不完但还可以再卖

{

sum+=d*save[i].price;

d=0;//表示剩下的需求全卖了此种月饼

}else break;//需求为0,退出

}

printf("%.2f",sum);

return 0;}1023 组个最小数 20 分)差不多一个思想,根据条件限制每一步都取最优。1030 完美数列 (25 分)给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 Mmp,则称这个数列是完美数列。现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列。输入格式:输入第一行给出两个正整数 N 和 p,其中 N≤10?5??)是输入的正整数的个数,p≤10?9??)是给定的参数。第二行给出 N 个正整数,每个数不超过 10?9??这里用到了two pointers 思想,虽然不多见,但还好理解。#include <stdio.h>#include <algorithm> using namespace std;int num[100010];int main(){

int n,p,l=1;

scanf("%d %d",&n,&p);

for(int i=0;i<n;i++) scanf("%d",&num[i]);

sort(num,num+n);//排序

int i=0,j=1;

  while(i<n&&j<n)//所检验的是从 i 到 j;  

  {

    while(j<n&&num[j]<=(long long)p*num[i])//题目中给的范围较大,所以结果转换成long long型

    {

      l=max(l,j-i+1);//更新最大长度

      j++;//右端点移动

    }

    i++;//左端点移动

  }

printf("%d",l);

return 0;}1035 插入与归并 (25 分)根据维基百科的定义:插入排序是迭代算法,逐一获得输入数据,逐步产生有序的输出序列。每步迭代中,算法从输入序列中取出一元素,将之插入有序序列中正确的位置。如此迭代直到全部元素有序。归并排序进行如下迭代操作:首先将原始序列看成 N 个只包含 1 个元素的有序子序列,然后每次迭代归并两个相邻的有序子序列,直到最后只剩下 1 个有序的序列。现给定原始序列和由某排序算法产生的中间序列,请你判断该算法究竟是哪种排序算法?输入格式:输入在第一行给出正整数 N (≤100);随后一行给出原始序列的 N 个整数;最后一行给出由某排序算法产生的中间序列。这里假设排序的目标序列是升序。数字间以空格分隔。输出格式:首先在第 1 行中输出Insertion Sort表示插入排序、或Merge Sort表示归并排序;然后在第 2 行中输出用该排序算法再迭代一轮的结果序列。题目保证每组测试的结果是唯一的。数字间以空格分隔,且行首尾不得有多余空格。这里考到了归并和插入排序,而且都需要实现,并比较每一步的结果。这里拿出来官方做法仅供参考。#include <cstdio>#include <algorithm> using namespace std;int a[110],b[110],t[110];int n;bool isSame(int a[],int b[]){

for(int i=0;i<n;i++) if(a[i]!=b[i]) return false;

return true;}void print(int t[]){

for(int i=0;i<n;i++)

{

printf("%d",t[i]);

if(i<n-1) printf(" ");

}}bool Insertion(){

bool flag=false;

for(int i=1;i<n;i++)

{

if(i!=1&&isSame(b,t)) flag=true;

int temp=t[i],j=i;

while(j>0&&t[j-1]>temp)

{

t[j]=t[j-1];

j--;

}

t[j]=temp;

if(flag) return true;

}

return false;}void Merge(){

bool flag=false;

for(int step=2;step/2<=n;step*=2)

{

if(step!=2&&isSame(t,b)) flag=true;

for(int i=0;i<n;i+=step)

{

sort(t+i,t+min(i+step,n));

}

if(flag)

{

print(t);return;

}

}}int main(){

scanf("%d",&n);

for(int i=0;i<n;i++)

{

scanf("%d",&a[i]);t[i]=a[i];

}

for(int i=0;i<n;i++) scanf("%d",&b[i]);

if(Insertion())

{

printf("Insertion Sort ");

print(t);

}else

{

printf("Merge Sort ");

for(int i=0;i<n;i++) t[i]=a[i];

Merge();

}

return 0;}1045 快速排序 (25 分)著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 给定划分后的 N 个互不相同的正整数的排列,请问有多少个元素可能是划分前选取的主元?例如给定 $N = 5$, 排列是1、3、2、4、5。则:

  • 1 的左边没有元素,右边的元素都比它大,所以它可能是主元;
  • 尽管 3 的左边元素都比它小,但其右边的 2 比它小,所以它不能是主元;
  • 尽管 2 的右边元素都比它大,但其左边的 3 比它大,所以它不能是主元;
  • 类似原因,4 和 5 都可能是主元。因此,有 3 个元素可能是主元。输入格式:输入在第 1 行中给出一个正整数 N≤10?5??); 2 行是空格分隔的 N 个不同的正整数,每个数不超过 10?9??输出格式:在第 1 行中输出有可能是主元的元素个数;在第 2 行中按递增顺序输出这些元素,其间以 1 个空格分隔,行首尾不得有多余空格。快速排序,不过只是找主元,根据定义#include <stdio.h>#include <algorithm>using namespace std;int n;int a[100010],b[100010],Max[100010],Min[100010];int main(){

  int max=0,min=1000000000;

  scanf("%d",&n);

  for(int i=0;i<n;i++)

  {

   scanf("%d",&a[i]);

   if(a[i]>max) max=a[i];

   Max[i]=max;

  }

  for(int i=n-1;i>=0;i--)

  {

   if(a[i]<min) min=a[i];

   Min[i]=min;

  }

  int j=0;

  for(int i=0;i<n;i++)

  {

    bool flag=true;

    if(Max[i]>a[i]) flag=false;

    if(Min[i]<a[i]) flag=false;

    if(flag) b[j++]=a[i];

  }

  if(j>1) sort(b,b+j);

  printf("%d ",j);

  if(j)

  {

   for(int i=0;i<j;i++)

    {

     if(i<j-1) printf("%d ",b[i]);

else printf("%d",b[i]);

   }

  }

  printf(" ");

  return 0;

  }

 

1040 有几个PAT (25 分)字符串 APPAPT 中包含了两个单词 PAT,其中第一个 PAT 是第 2 位(P),第 4 位(A),第 6 位(T);第二个 PAT 是第 3 位(P),第 4 位(A),第 6 位(T)。现给定字符串,问一共可以形成多少个 PAT输入格式:输入只有一行,包含一个字符串,长度不超过10?5??,只包含 PAT 三种字母。输出格式:在一行中输出给定字符串中包含多少个 PAT。由于结果可能比较大,只输出对 1000000007 取余数的结果。#include <stdio.h>#include <cstring>using namespace std;const int mod=1000000007;char str[100010];int pos[100010];int main(){

  scanf("%s",str);

  int l=strlen(str),j=0;

  for(int i=0;i<l;i++)//pos记录 i 位及左边有多少个 P;

  {

    if(i>0) pos[i]=pos[i-1];

    if(str[i]==‘P‘) pos[i]++;

  }

  int ans=0,right=0;

  for(int i=l-1;i>=0;i--)

  {

    if(str[i]==‘T‘) right++;//倒序遍历,找第 i 位及右边的 T 的个数;

    else if(str[i]==‘A‘)

    {

      ans=(ans+pos[i]*right)%mod;//发现 A 时 则左右可组成不重复的若干PAT

    }

  }

  printf("%d ",ans);

  return 0;}

 

1015 德才论 (25 分)宋代史学家司马光在《资治通鉴》中有一段著名的“德才论”:“是故才德全尽谓之圣人,才德兼亡谓之愚人,德胜才谓之君子,才胜德谓之小人。凡取人之术,苟不得圣人,君子而与之,与其得小人,不若得愚人。”现给出一批考生的德才分数,请根据司马光的理论给出录取排名。输入格式:输入第一行给出 3 个正整数,分别为:N≤10?5??),即考生总数;L≥60),为录取最低分数线,即德分和才分均不低于 L 的考生才有资格被考虑录取;H<100),为优先录取线——德分和才分均不低于此线的被定义为“才德全尽”,此类考生按德才总分从高到低排序;才分不到但德分到线的一类考生属于“德胜才”,也按总分排序,但排在第一类考生之后;德才分均低于 H,但是德分不低于才分的考生属于“才德兼亡”但尚有“德胜才”者,按总分排序,但排在第二类考生之后;其他达到最低线 L 的考生也按总分排序,但排在第三类考生之后。随后 N 行,每行给出一位考生的信息,包括:准考证号 德分 才分,其中准考证号 8 位整数,德才分为区间 [0, 100] 内的整数。数字间以空格分隔。输出格式:输出第一行首先给出达到最低分数线的考生人数 M,随后 M 行,每行按照输入格式输出一位考生的信息,考生按输入中说明的规则从高到低排序。当某类考生中有多人总分相同时,按其德分降序排列;若德分也并列,则按准考证号的升序输出。

 

#include <algorithm>#include <cstdio>#include <cstring>using namespace std;struct all{

char num[10];

int de;

int cai;

int sum;

int kind;}data[100010];bool cmp(all a,all b){

if(a.kind!=b.kind) return a.kind<b.kind;

else if(a.sum!=b.sum) return a.sum>b.sum;

else if(a.de!=b.de) return a.de>b.de;

else return strcmp(a.num,b.num)<0;}int main(){

  int n,low,high;

  scanf("%d %d %d",&n,&low,&high);

  int s=0;

  for(int i=0;i<n;i++)

  {

   scanf("%s %d %d",data[i].num,&data[i].de,&data[i].cai);

   data[i].sum=data[i].cai+data[i].de;

   if(data[i].cai<low||data[i].de<low)

   {

   s++;data[i].kind=5;

}

   else if(data[i].cai>=high&&data[i].de>=high) data[i].kind=1;

   else if(data[i].cai>=low&&data[i].de>=high) data[i].kind=2;

   else if(data[i].cai<high&&data[i].cai>=low&&data[i].de>=data[i].cai) data[i].kind=3;

   else data[i].kind=4;   

  }

  printf("%d ",n-s);

  sort(data,data+n,cmp);

  

  for(int i=0;i<n;i++)

  {

   if(data[i].kind<5) printf("%s %d %d ",data[i].num,data[i].de,data[i].cai);

else break;

  }

  return 0;}

 

1055 集体照 (25 分)拍集体照时队形很重要,这里对给定的 N 个人 K 排的队形设计排队规则如下:每排人数为 N/K(向下取整),多出来的人全部站在最后一排;后排所有人的个子都不比前排任何人矮;每排中最高者站中间(中间位置为 m/2+1,其中 m 为该排人数,除法向下取整);每排其他人以中间人为轴,按身高非增序,先右后左交替入队站在中间人的两侧(例如5人身高为190、188、186、175、170,则队形为175、188、190、186、170。这里假设你面对拍照者,所以你的左边是中间人的右边);若多人身高相同,则按名字的字典序升序排列。这里保证无重名。现给定一组拍照人,请编写程序输出他们的队形。输入格式:每个输入包含 1 个测试用例。每个测试用例第 1 行给出两个正整数 N≤10?4??,总人数)和 K≤10,总排数)。随后 N 行,每行给出一个人的名字(不包含空格、长度不超过 8 个英文字母)和身高([30, 300] 区间内的整数)。输出格式:输出拍照的队形。即K排人名,其间以空格分隔,行末不得有多余空格。注意:假设你面对拍照者,后排的人输出在上方,前排输出在下方。//将所有人按规则排好,然后一个一个的往位置表里放,注意表是按摄影师角度的,所以最上边//的一排是最高的同学。 #include <stdio.h>#include <algorithm>#include <string.h>using namespace std; int n,k;struct st//学生信息 {

char name[10];

int height;}all[10010];st p[11][10010];//站队的位置表 bool cmp1(st a,st b){

if(a.height==b.height) return strcmp(a.name,b.name)<0;

else return a.height>b.height;}int main(){

scanf("%d%d",&n,&k);

int pai=n/k,res=n%k;//排数和多出来站最后一排的人数

for(int i=0;i<n;i++) scanf("%s %d",all[i].name,&all[i].height);

sort(all,all+n,cmp1);

int step=-1,j=0;//注意对于照相者而说,是先往左放  

for(int i=(pai+res)/2;j<pai+res;)//先站最后一排

{

if(i<pai+res&&i>=0) //满足最后一排的条件

{

strcpy(p[0][i].name,all[j].name);j++;

}

i+=step;

if(step>0) step++;

else step--;//加大步长

step*=-1;//反向

}

int count=0;

for(int i=1;i<k;i++)//放其他排

{

step=-1;count=0;

for(int l=pai/2;count<pai;)

{

if(l<pai&&l>=0)

{

strcpy(p[i][l].name,all[j].name);j++;count++;

}

l+=step;

if(step>0) step++;

else step--;

step*=-1;

}

}

for(int i=0;i<pai+res;i++)//输出

{

if(i==0) printf("%s",p[0][i].name);

else printf(" %s",p[0][i].name);

}

printf(" ");

for(int i=1;i<k;i++)

{

  

for(j=0;j<pai;j++)

{

if(j==0) printf("%s",p[i][j].name);

else printf(" %s",p[i][j].name);

}

printf(" ");

}

return 0;}1080 MOOC期终成绩 (25 分)对于在中国大学MOOC(http://www.icourse163.org/ )学习“数据结构”课程的学生,想要获得一张合格证书,必须首先获得不少于200分的在线编程作业分,然后总评获得不少于60分(满分100)。总评成绩的计算公式为 G=(G?mid?term??×40%+G?final??×60%),如果 G?mid?term??>G?final??;否则总评 G 就是 G?final??。这里 G?mid?term?? 和 G?final?? 分别为学生的期中和期末成绩。现在的问题是,每次考试都产生一张独立的成绩单。本题就请你编写程序,把不同的成绩单合为一张。输入格式:输入在第一行给出3个整数,分别是 P(做了在线编程作业的学生数)、M(参加了期中考试的学生数)、N(参加了期末考试的学生数)。每个数都不超过10000。接下来有三块输入。第一块包含 P 个在线编程成绩 G?p??;第二块包含 M 个期中考试成绩 G?mid?term??;第三块包含 N 个期末考试成绩 G?final??。每个成绩占一行,格式为:学生学号 分数。其中学生学号为不超过20个字符的英文字母和数字;分数是非负整数(编程总分最高为900分,期中和期末的最高分为100分)。输出格式:打印出获得合格证书的学生名单。每个学生占一行,格式为:学生学号 G?p?? G?mid?term?? G?final?? G如果有的成绩不存在(例如某人没参加期中考试),则在相应的位置输出“?1”。输出顺序为按照总评分数(四舍五入精确到整数)递减。若有并列,则按学号递增。题目保证学号没有重复,且至少存在1个合格的学生。

 

本来用的map,先存下所有成绩然后再统一计算,再排序,参考了大神的做法后如下,大概思路还是那样#include <iostream>#include <string>#include <map>#include <algorithm>using namespace std;map<string,int> fen;struct record{

string name;

int score;

int gp,gm,gf;}list[10010];bool cmp(record a,record b){

if(a.score==b.score) return a.name<b.name;

else return a.score>b.score;}int p,m,n;int main(){

cin>>p>>m>>n;

string str;int x,j=0;

for(int i=0;i<p;i++)

{

cin>>str>>x;

if(x>=200)//只存200以上的信息,并把期中期末置-1,总分为0,并记录下此同学的编号 j  

{

list[j].name=str;list[j].gp=x;list[j].gm=-1;list[j].gf=-1;list[j].score=0;

fen[str]=j+1;j++;//此处记录的是j+1,为了避免0的冲突

}

}

//到这里,j 就是满足200以上的人数

for(int i=0;i<m;i++)//输入期中成绩

{

cin>>str>>x;

if(fen[str]!=0)

{

list[fen[str]-1].gm=x;

}

}

for(int i=0;i<n;i++)

{

cin>>str>>x;

if(fen[str]!=0)

{

list[fen[str]-1].gf=x;

//然后就计算其总分

list[fen[str]-1].score=x;//

if(list[fen[str]-1].gm>list[fen[str]-1].gf)//期中成绩有效时

{

list[fen[str]-1].score=int(0.4*list[fen[str]-1].gm+0.6*list[fen[str]-1].gf+0.5);

}

}

}

sort(list,list+j,cmp);//排序后不合格的一定往后排了,而且没有期末考试的一定不及格

for(int i=0;i<j;i++)

{

if(list[i].score<60) break;

cout<<list[i].name<<‘ ‘<<list[i].gp<<‘ ‘<<list[i].gm<<‘ ‘<<list[i].gf<<‘ ‘<<list[i].score<<endl;

}

return 0;}1085 PAT单位排行 (25 分)每次 PAT 考试结束后,考试中心都会发布一个考生单位排行榜。本题就请你实现这个功能。输入格式:输入第一行给出一个正整数 N(≤10?5??),即考生人数。随后 N 行,每行按下列格式给出一个考生的信息:准考证号 得分 学校其中准考证号是由 6 个字符组成的字符串,其首字母表示考试的级别:B代表乙级,A代表甲级,T代表顶级;得分 [0, 100] 区间内的整数;学校是由不超过 6 个英文字母组成的单位码(大小写无关)。注意:题目保证每个考生的准考证号是不同的。输出格式:首先在一行中输出单位个数。随后按以下格式非降序输出单位的排行榜:排名 学校 加权总分 考生人数其中排名是该单位的排名(从 1 开始);学校是全部按小写字母输出的单位码;加权总分定义为乙级总分/1.5 + 甲级总分 + 顶级总分*1.5整数部分考生人数是该属于单位的考生的总人数。学校首先按加权总分排行。如有并列,则应对应相同的排名,并按考生人数升序输出。如果仍然并列,则按单位码的字典序输出。#include <iostream>#include <algorithm>#include <map>using namespace std;struct sc{

string name;

int score;

int sum;}rr[100000];map<string,double> mp,ms;bool cmp(sc a,sc b){

int a1=a.score,b1=b.score;

if(a1==b1)

{

if(a.sum==b.sum) return a.name<b.name;

else return a.sum<b.sum;

}else return a1>b1;} int main(){

string a,b;

int n,x,j=1;

cin>>n;

for(int i=0;i<n;i++)

{

cin>>a>>x>>b;

for(int j=0;j<b.size();j++)

{

if(b[j]<=‘Z‘) b[j]+=32;

}

double s;

if(a[0]==‘A‘) s=x;

if(a[0]==‘B‘) s=1.0*x/1.5;

if(a[0]==‘T‘) s=1.5*x;

mp[b]++;ms[b]+=s;

}

int t=0;

for(map<string,double>::iterator it=mp.begin();it!=mp.end();it++)

{

rr[t].name=it->first;rr[t].sum=it->second;rr[t].score=ms[it->first];t++;

}

sort(rr,rr+t,cmp);

cout<<t<<endl;

int num=1;

for(int i=0;i<t;i++)

{

if(i>0&&rr[i].score==rr[i-1].score)

{

cout<<num;

}else

{

num=i+1;cout<<num;

}

cout<<‘ ‘<<rr[i].name<<‘ ‘<<rr[i].score<<‘ ‘<<rr[i].sum<<endl;

}

return 0;}

最后这一题慌里慌张练了一下,大致和上一题差不多,注意用double型存成绩,到最后再化整形。

 

 

以上是关于PAT乙级考前总结(三)的主要内容,如果未能解决你的问题,请参考以下文章

PAT乙级15分题易错题总结

PAT乙级题库全套总结

PAT 乙级 1045

PAT 乙级 1044 火星数字

PAT 乙级 1040.有几个PAT C++/Java

PAT乙级1088