2021年 西南石油大学超算与并行计算团队南充校区分队 第二届招新赛题解
2021年SWPU(南充)超算团队招新赛总体难度并不是很大,大部分题目考察的是基本的编程能力,题目中涉及到了一些并行计算相关的名词和知识,选手在参加比赛的同时,既能够展示自己的实力,也可以学习到相关的一些知识。下面是本次招新赛的题目
A.简单输出
题目描述:
题目要求:
输出一句看起来就很重要的话。
难度:天梯赛L1-1
知识点:输入输出格式化控制
题解:这个题的背景便是团队的一个基本的介绍,主要是带领选手们了解超算团队,题目要求很简单,其实就是输出一句话,那句话已经在题目中加粗标出了哦,直接输出就行。
参考代码:
#include<stdio.h> int main() { printf("超算与并行计算团队"); return 0; }
B.旅行预算
题目描述:
题目要求:
难度:天梯赛L1-2
知识点:简单的数学运算
题解:这个题的背景是2021年ACM-IPCC比赛的故事,很可惜这场现场赛因为疫情改成了线上,主要是带领选手们了解超算比赛,激发选手们的兴趣,题目要求便是一个参赛费用的计算,首先先计算出6个人机票的费用,注意是往返要乘2,住宿是2晚,6个人需要3间房间,计算出住宿的费用后,相加即可得到正确答案。
参考代码:
#include<stdio.h> int main() { int F,H; scanf("%d %d",&F,&H); int W; W=F*6*2+H*(6/2)*2; printf("%d\n",W); return 0; }
C.SLIC超像素分割法
题目描述:
题目要求:
难度:天梯赛L1-3
知识点:简单计算+if分支判断
题解:这个题的背景超像素算法是2021年ACM-IPCC比赛赛题的内容,比赛的内容是对超像素算法进行优化,加入团队后大家可以学习这方面的知识,题目的内容只是比赛的一个很小的一部分,判断像素点与聚类中心之间的相关性。首先用距离公式计算出他们之间的距离,再使用if按照题目中的描述判断相关性即可
参考代码:
#include<stdio.h> #include<math.h> int main() { int x,y,s; int a,b; double t; scanf("%d %d %d",&x,&y,&s); scanf("%d %d",&a,&b); t=sqrt((x-a)*(x-a)+(y-b)*(y-b)); if (t<=s) { printf("Belong To C\n"); } else if (t>s && t<=2*s) { printf("Similar To C\n"); } else { printf("None Of C\n"); } return 0; }
D.字母统计
题目描述:
难度:天梯赛L1-4
知识点:字符串
题解:这个题的话,主要考察字符串,pra便是超算的简写哦(parallel),挨着判断字符串中每个字母,统计其个数就好
参考代码:
#include<stdio.h> #include<string.h> int main() { char s[5000]; int p=0,r=0,a=0,i; gets(s); for (i=0;i<strlen(s);i++) { if (s[i]=='p') { p++; } if (s[i]=='r') { r++; } if (s[i]=='a') { a++; } } printf("P: %d\nR: %d\nA: %d",p,r,a); return 0; }
E.进制转换
题目描述:
题目要求:
难度:天梯赛L1-5
知识点:字符串,进制转换
题解:这个题的话,主要是带领选手了解计算机网络和位运算相关的知识,计算机网络是计算机专业基础课之一哦,在算法优化过程中,如果能使用位运算,会提高程序运行的效率。按照题目的要求,使用字符串先保存下这32位的数据,将32位的数据按照8位一组划分进行计算,划分出的每组8位数字视为一个二进制数据,按照二进制转换为十进制的方法进行计算,得到四个十进制数据,依次输出,即可完成划分。
参考代码:
#include<stdio.h> int main() { char s[40]; gets(s); int b=128,i; int ans=0; for (i=0;i<8;i++) { ans=ans+(s[i]-48)*b; b=b/2; } printf("%d.",ans); b=128,ans=0; for (i=8;i<16;i++) { ans=ans+(s[i]-48)*b; b=b/2; } printf("%d.",ans); b=128,ans=0; for (i=16;i<24;i++) { ans=ans+(s[i]-48)*b; b=b/2; } printf("%d.",ans); b=128,ans=0; for (i=24;i<32;i++) { ans=ans+(s[i]-48)*b; b=b/2; } printf("%d",ans); return 0; }
F.进程调度
题目描述:
题目要求:
难度:天梯赛L1-4
知识点:数组,阅读理解(操作系统)
题解:这个题目的描述涉及计算机操作系统里进程调度方面的内容,描述中给出的三种进程调度的方式是计算机基本的三种进程调度的方式,在超算中,灵活控制进程进行调度,可以减少程序的运行时间,从而使程序得到优化。这个题是个简单的模拟题,模拟出先来先服务调度方式(就是让数组中的元素一个一个的离开)即可。
参考代码:
#include<stdio.h> int main() { int n; scanf("%d",&n); int i,j,a[25]; for (int i=0;i<n;i++) { scanf("%d",&a[i]); } for (i=0;i<n;i++) { printf("Now:%d Wait:",a[i]); for (j=i+1;j<n;j++) { if (j==i+1) { printf("%d",a[j]); } else { printf(" %d",a[j]); } } printf("\n"); } return 0; }
G.团队练习(上集)
题目描述:
题目要求:
难度:天梯赛L1-6
知识点:结构体
题解:这个题的背景是超算比赛的比赛方式,目的是让选手们了解超算比赛是一个怎样的比赛。主要是考察结构体的使用,需要将学生的学号和成绩(程序的运行时间)记录下来,找出运行时间最短的学生,输出他的学号即可。(当然也可以不使用结构体,记录下最佳运行时间和其对应的学生信息即可)
参考代码:
#include<stdio.h> struct pa{ char s[15];//开 long long 也行 int score; }PRA[100]; int main() { int n,i; scanf("%d\n",&n); for (i=0;i<n;i++) { if (i==n-1) { scanf("%s %d",PRA[i].s,&PRA[i].score); } else { scanf("%s %d\n",PRA[i].s,&PRA[i].score); } } int min=PRA[0].score; int flag=0; for (i=1;i<n;i++) { if (PRA[i].score<min) { min=PRA[i].score; flag=i; } } printf("%s",PRA[flag].s); return 0; }
H.团队练习(下集)
题目描述:
题目要求:
难度:天梯赛L1-6+
知识点:排序
题解:这个题的考点是排序,为了增加难度,于是便要求将排名相同的同学进行特殊处理,其难度也不是很大,排序结束后,判断排名在第i个同学的成绩是否与第i-1个同学的成绩相同即可,如果相同,就输出相同的排名,在计算排名前10%的同学时,按照要求先计算出礼物的数量,再用同样的方法处理排名并列的情况即可(有个小坑,n的值可能为0,需要单独处理)
参考代码:
#include<stdio.h> int main() { int n,i,j; int a[200]; scanf("%d",&n); for (i=0;i<n;i++) { scanf("%d",&a[i]); } for (i=0;i<n;i++) { for (j=i;j<n;j++) { if (a[i]>a[j]) { int t; t=a[i]; a[i]=a[j]; a[j]=t; } } } for (i=0;i<n;i++) { int ii=0; while (a[i]==a[i+ii]) { printf("RANK%d:%d\n",i+1,a[i+ii]); ii++; } i=i+ii-1; } int NN; if (n==0) { printf("学长需准备0份礼物\n"); } else { if(n%10==0) { NN=n/10; } else { NN=n/10+1; } int ii=0; while (a[NN-1]==a[NN+ii]) { ii++; } NN=ii+NN; printf("学长需准备%d份礼物\n",NN); } return 0; }
I.质数火车
题目描述:
题目要求:
难度:天梯赛L1-7
知识点:思维、素数
题解:素数判断是基本的一个算法,根据题意,我们只需要找出从2开始计算的素数和小于l时的所有素数,统计选出的素数的个数即可。
参考代码:
#include<bits/stdc++.h> using namespace std; int n,x; long long sum=0; int pd(int y) { for(int i=2; i*i<=y; ++i) { if(y%i==0) return 0; } return 1; } int main() { scanf("%d",&n); if(n<2) { printf("0\n"); return 0; } else if(n==2) { printf("2\n1\n"); return 0; } for(int i=2; i<=n; ++i) { if(i%2==0&&i!=2) continue; if(sum+i>n) { printf("%d\n",x); return 0; } if(pd(i)) { printf("%d\n",i); sum+=i; x++; } } return 0; }
J.寻找你的幸运之门
题目描述:
题目要求:
难度:天梯赛L1-7
知识点:思维
题解:看图,可以发现题目中门的排列是不是像一颗二叉树,我们可以采用二进制的思想来简化这个问题,二进制的两个数字正好就能表示向左和向右走的两个状态,用0表示向左走,1表示向右走,从第1扇门开始存储,依次存放数据,这样,得到的二叉树的根节点为数据的最高位,而叶子节点则是二进制的最低位,最后得到的这个二进制表示的数字,就是幸运之门的编号。
参考代码:
#include<stdio.h> #include<math.h> int main() { int n,l,i,J; scanf("%d %d\n",&n,&l); for (J=0;J<l;J++) { char s[50]; gets(s); long long ans=0; long long b=pow(2,n-1); for (i=0;i<n;i++) { if (s[i]=='R') { ans=ans+b; } b=b/2; } printf("%lld\n",ans+1); } return 0; }
K.MPI(Message Passing Interface),消息传递接口
题目描述:
题目要求:
难度:天梯赛L1-8
知识点:阅读理解,字符串处理
题解:这个题的题面提及了MPI技术,MPI技术主要是用于进程间通信的,在短时间内难以理解,这个题目的要求是使用0号进程来完成其他进程的规约操作,换言之,就是使用0号进程将其他进程的结果使用相应的规约原语(MPI_SUM)结合起来,原语是用来实现某个功能的语句,题目中给出的MPI_SUM就是求和的意思,按照进程的编号,依次进行求和即可。首先,我们要跟据字符串,判断原语的种类和数据的类型。MPI_INT和MPI_DOUBLE对应的int和double类型直接进行加法计算即可,MPI_CHAR对应的char类型比较难处理一点。则需要暂时将消息的内容记下来,再跟据进程编号,依次将消息连接起来。MPI技术也是并行计算的核心技术之一,也是加入团队后需要深入学习的内容。
#include<stdio.h> #include<string.h> int main() { char S[12][50]; int n,i; scanf("%d\n",&n); char a[15]; gets(a); if (a[4]=='I' && a[5]=='N' && a[6]=='T') { int sum=0; for (i=0;i<n-1;i++) { int s1,s2; scanf("%d %d\n",&s1,&s2); sum=sum+s2; } char z[15]; gets(z); if (z[4]=='S' && z[5]=='U' && z[6]=='M') { printf("%d\n",sum); } } if (a[4]=='D' && a[5]=='O') { double sum=0; for (i=0;i<n-1;i++) { double s1,s2; scanf("%lf %lf\n",&s1,&s2); sum=sum+s2; } char z[15]; gets(z); if (z[4]=='S' && z[5]=='U' && z[6]=='M') { printf("%.2f\n",sum); } } if (a[4]=='C' && a[5]=='H') { for (i=0;i<n-1;i++) { int n; scanf("%d ",&n); scanf("%s",S[n-1]); } char z[15]; scanf("%s",z); if (z[4]=='S' && z[5]=='U' && z[6]=='M') { for (i=0;i<n-1;i++) { printf("%s",S[i]); if (i!=n-1) { printf(" "); } } } } return 0; }
L.遥控机器人
题目描述:
题目要求:
难度:天梯赛L1-8
知识点:模拟
题解:这个题的本质是一个模拟题,按照题目要求,模拟机器人的移动即可。在题目中,提及了指令集的概念,指令便是指挥机器工作的指令,偏向计算机底层和它的组成原理。而在超级计算机上研究高性能计算,也要对超级计算机的组成原理有所了解,才可能在项目中起到事半功倍的效果。
参考代码:
#include<stdio.h> int main() { int i,N; long long x=0,y=0; int flag=0; scanf("%d",&N); for (i=0;i<N;i++) { int op; scanf("%d",&op); if (op==0) { flag=1; } if (op==-1) { flag=0; } if (op==1) { int imm; scanf("%d",&imm); if (flag==1) { y=y+imm; } } if (op==2) { int imm; scanf("%d",&imm); if (flag==1) { y=y-imm; } } if (op==3) { int imm; scanf("%d",&imm); if (flag==1) { x=x-imm; } } if (op==4) { int imm; scanf("%d",&imm); if (flag==1) { x=x+imm; } } } printf("(%d,%d)\n",x,y); return 0; }
M.水果
题目描述:
题目要求:
难度:天梯赛L2-1
知识点:贪心算法
题解:这个题应该是全场到目前为止第一个涉及算法的题目,题目描述看上去像个背包,但由于“可以允许取出一部分库存”,故贪心即可求解这个问题,按照总售价/库存就可以得到单位水果的售价,对其进行排序,从高到低进行选择即可。其实,在高性能计算优化过程中,也会涉及到算法优化的。
参考代码:
#include<stdio.h> #include<bits/stdc++.h> using namespace std; struct a{ double x; double y; double v; }gj[110]; bool cmp(a s1,a s2) { return s1.v>s2.v; } int main() { int n,s,i; scanf("%d %d",&n,&s); for (i=0;i<n;i++) { scanf("%lf",&gj[i].x); } for (i=0;i<n;i++) { scanf("%lf",&gj[i].y); } for (i=0;i<n;i++) { gj[i].v=gj[i].y/gj[i].x; } sort (gj,gj+n,cmp); double money=0; for (i=0;i<n;i++) { if (gj[i].x<=s) { s=s-gj[i].x; money=money+gj[i].y; continue; } else { money=money+gj[i].y*(s/gj[i].x); break; } } printf("%.2f",money); return 0; }
N.HPC(高性能计算机群)
题目描述:
题目要求:
难度:天梯赛 L2-2
知识点:DFS搜索
题解:防AK题,这个题的背景是HPC超算集群,但题目要求跟这个没啥关系,了解一下即可。题目要求是选择一个节点,寻找其他节点到该节点的最小距离和,我们可以采用深度优先搜索的方式,将每一个节点设置为起点,在这颗集群树上依次按层次搜索,计算,比较得到最小距离和即可。
参考代码:
#include<stdio.h> #include<string.h> struct tree{ int val; int left; int right; int parent; }Tree[1024]; int vis[1024]={0}; int bestans=-1; int value; void dfs(int i,int deep) { vis[i]=1; value=value+deep*Tree[i].val; if (Tree[i].parent!=0 && vis[Tree[i].parent]==0)//搜索父节点 { dfs(Tree[i].parent,deep+1); } if (Tree[i].left!=0 && vis[Tree[i].left]==0)//搜索左子节点 { dfs(Tree[i].left,deep+1); } if (Tree[i].right!=0 && vis[Tree[i].right]==0)//搜索右子节点 { dfs(Tree[i].right,deep+1); } } int main() { int n,i; scanf("%d",&n); for (i=1;i<=n;i++) { scanf("%d %d %d",&Tree[i].val,&Tree[i].left,&Tree[i].right); if (Tree[i].left!=0)//由子节点计算出对应的父节点进行保存,便于搜索 { Tree[Tree[i].left].parent=i; } else { Tree[Tree[i].left].parent=0; } if (Tree[i].right!=0) { Tree[Tree[i].right].parent=i; } else { Tree[Tree[i].right].parent=0; } } for (i=1;i<=n;i++) { value=0; memset(vis,0,sizeof(int)*n+1); dfs(i,0); if (bestans==-1 || bestans>value) { bestans=value; } } printf("%d\n",bestans); }
比赛结果如下(满分200分,只截取了120分以上的部分,一人AK,还是不错的哦~):