实验题:
第一题:化学
1、题意简介:一类烷烃基有6个原子和5个化学键,6个原子分别标号1~6,然后用一对数字 a,b 表示原子a和原子b间有一个化学键。通过5行a,b可以描述一个烷烃基。要求给出五行a和b的数值,判断属于哪一种烷烃基。
2、做法思路:
烷烃基种类如下图:
通过观察六种烷烃基的特点,发现:
n-hexane六个原子都连接有一个或两个化学键;
2-methylpentane和3-methyplentane中有一个原子连接有三个化学键;
2,3-dimethylutane中有两个原子连接有三个化学键;
2,2-dimethylbutane中有一个原子连接有四个化学键。
因此可以通过判断烷烃基中连接三个或三个以上化学键的原子的个数初步区分出n-hexane、2,3-dimethylutane和2,2-dimethylbutane,而2-methylpentane和3-methyplentane则需要进一步判断其他原子的连接特点,即判断连接三个化学键的原子x直接连接的三个相邻原子a,b,c的化学键特点,若a,b,c中只有一个原子仅连接一个化学键,则是3-methyplentane;若有两个原子仅连接一个化学键,则是2-methylpentane。
3、代码:
#include<iostream> using namespace std; int main() { int t; cin>>t; for(int i=0;i<t;i++) { int a[7][7]={0}; int length[7]={0}; for(int i=0;i<5;i++) {//记录化学键的位置和每个原子所连接的化学键数量 int x,y; cin>>x>>y; a[x][y]=1; a[y][x]=1; length[x]++; length[y]++; } int b=0;//记录连接三个化学键的原子数目 int c=0;//记录连接四个化学键的原子数目 for(int i=1;i<7;i++) { if(length[i]==3) b++; else if(length[i]==4) c++; } //判断烷烃基种类 if(b==0&&c==0) {//没有分支 cout<<"n-hexane"<<endl; } else if(b==0&&c==1) {//有一个原子连接了四个化学键 cout<<"2,2-dimethylbutane"<<endl; } else if(b==2&&c==0) {//有两个原子连接了三个化学键 cout<<"2,3-dimethylbutane"<<endl; } else if(b==1&&c==0) {//有一个原子连接了三个化学键,此时有两种情况 int num[4];//记录连接三个化学键的原子序号 for(int i=1;i<7;i++) { if(length[i]==3) { num[0]=i; break; } } //记录与num1直接相连的三个原子的序号 int j=1; for(int i=1;i<7;i++) { if(a[num[0]][i]==1) { num[j]=i; j++; } } int d=0; for(int i=1;i<4;i++) { if(length[num[i]]==2) d++; } if(d==1) cout<<"2-methylpentane"<<endl; else if(d==2) cout<<"3-methylpentane"<<endl; } } return 0; }
第二题:实时评测系统成绩排序
1.题意简介:每个学生有一组时间数据,为负数表示该题目该名学生还没有AC,为正数表示AC所耗时间,如果正数a跟上了一对括号,里面有个正数b,则表示该学生AC了这道题,耗去了时间a,同时曾经错误提交了b次。要求根据学生得分情况,输出排名。
得分计算方式为:负数不计分,若正数a后有整数b,则该题耗时为a+b*罚时。
输出格式:每个学生占一行,输出名字(10个字符宽),做出的题数(2个字符宽,右对齐)和时间分(4个字符宽,右对齐)。名字、题数和时间分相互之间有一个空格。
2.做法思路:
本题首先考察了对输入字符串的判断,需要将姓名、负数、正数以及正数后的括号内容区分开,此处我采用一个student类,类内包含学生姓名string name,AC题目数量int Acnum以及总耗时int time
此外,为区分字符串内容,我选择用char类型的数据etime表示每一题的时间输入,通过判断etime[i]的字符来区分数据类型(即判断‘-’、'('、')'这类特殊的字符),循环计算每一题的时间,之后得出总时间,判断进入循环的条件是输入的姓名不为空。
3.代码:
#include<iostream> #include<cstring> #include<stdio.h> #include<cstdlib> #include<algorithm> using namespace std; struct student {//学生类 int Acnum;//AC的题目数量 int time;//总耗时 string name; }; bool compare(const student &m,const student &n) { if(m.Acnum<n.Acnum) return false; if(m.Acnum==n.Acnum) { if(m.time>n.time) return false; if (m.time==n.time) { if (m.name>n.name) return false; return true; } return true; } return true; } int main() { student s[1000];//创建学生对象 for(int i=0;i<1000;i++) { s[i].Acnum=0; s[i].time=0; } int n; int wtime; cin>>n>>wtime;//获得题目数目和罚时 char etime[20]; //每一题的时间 int sumtime;//计算过后的总时间 int x=0; while (cin>>s[x].name) { for(int i=0;i<n;i++) { sumtime=0; int j=0; cin>>etime; if(etime[0]!='-') {//时间不为负,表示该题目已AC,可以计算时间 sumtime=etime[j]-'0'; j++; while (etime[j]>='0' && etime[j]<='9') {//计算AC所用时间 sumtime=sumtime*10+etime[j]-48; j++; } if (etime[j]=='(') {//判断后面是否有括号 j++; int wrtime=0; while (etime[j]!=')' && etime[j]!=' ') {//计算总罚时 wrtime=wrtime*10+etime[j]-48; j++; } sumtime=sumtime+wrtime*wtime; } if (etime[0]!='0') s[x].Acnum++; } s[x].time+=sumtime; } x++; } //cout<<"1"<<endl; sort(s,s+x,compare); for(int i=0;i<x;i++) {//按要求格式输出 printf("%-10s %2d %4d\n",s[i].name.c_str(),s[i].Acnum,s[i].time); //cout<<s[i].name<<" "<<s[i].Acnum<<" "<<s[i].time<<endl; } return 0; }
第三题:扑克牌排序
1.题意简介:有四名玩家:South player、West player、North player、East player。扑克牌花色为(梅花)<(方片)<(黑桃)<(红桃)(分别用C、D、S、H来表示),牌面的值规定2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < T < J < Q < K < A。
开始时输入一个大写字母,规定首字母为该大写字母的玩家为发牌员,之后按顺时针方向开始发牌(第一张牌发给发牌员的下一位),接下来两行,每行输入26个字符,两个为一组,前一个字符为花色,后一个字符为数字。同时输入多组发牌顺序,以#结束。
要求输出:按顺时针顺序输出,总是从South Player开始输出,具体格式如下:
2.做法思路:
本题考查对输入非数字字符的排序,按要求的格式输出和同时进行多组不同类型数据的存储、排序的能力。
考虑到一次需要同时存储四个人的扑克数据,所以我首先创建了一个card类,包含卡片颜色char color和卡片数字char num,之后申请了一个card类型的二维数组cards[4][13],表示总共四位玩家,每位玩家每次可以发得13张牌,用于记录四位玩家的卡牌数据。
之后考虑到数字和花色的排序问题,根据题意设计函数getcolNum(),把花色从小到大转换为数字1、2、3、4,对于数字中的T、J、Q、K、A也是同样的方法,将其用函数getnumber()转换为10、11、12、13、14。为了方便排序,我使用了sort()函数,但是需要重载<操作符,重载规则为当花色相同时,判断数字大小,花色不同是直接判断花色大小,重载完操作符后就可以直接使用sort()函数进行排序了。
3.代码:
#include<stdio.h> #include<iostream> #include<algorithm> using namespace std; //C:梅花,D:方片,S:黑桃,H:红桃 struct card {//卡片类 char color;//卡片的花色 char num;//卡片的数字 } ; card cards[4][13];//总共4个玩家,每个玩家可分得13张牌 //卡牌花色 ,C<D<S<H int getcolNum(char a) {//把花色转换为数字 switch(a) { case 'C': return 1; case 'D': return 2; case 'S': return 3; case 'H': return 4; } } //卡牌数字排序:2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < T < J < Q < K < A int getnumber(char a) {//把英文字符转换成数字 if(a<='9')return a-'0'; switch(a) { case 'T': return 10; case 'J': return 11; case 'Q': return 12; case 'K': return 13; case 'A': return 14; } } bool operator < (card& a,card& b) {//重载操作符 if(getcolNum(a.color)==getcolNum(b.color)) { return getnumber(a.num)<getnumber(b.num); } else { return getcolNum(a.color)<getcolNum(b.color); } } void printplayer(int i) { switch(i) { case 0: printf("South player:\n"); return; case 1: printf("West player:\n"); return; case 2: printf("North player:\n"); return; case 3: printf("East player:\n"); return; default: printf("no player:\n"); return; } } void printCards(card* s) {//按要求格式输出卡牌 printf("+---+---+---+---+---+---+---+---+---+---+---+---+---+"); for(int i=0; i<13; i++) { if(i==0) { printf("\n|%c %c|",s[i].num,s[i].num); } else { printf("%c %c|",s[i].num,s[i].num); } } for(int i=0; i<13; i++) { if(i==0) { printf("\n| %c |",s[i].color); } else { printf(" %c |",s[i].color); } } for(int i=0; i<13; i++) { if(i==0) { printf("\n|%c %c|",s[i].num,s[i].num); } else { printf("%c %c|",s[i].num,s[i].num); } } printf("\n+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"); } int main() { char n; while(~scanf("%c",&n) && n!='#')//获取发牌员 { getchar();//消除回车 int id; switch(n)//id为发牌员的下一位 { case 'S':{ id=1; break; } case 'W':{ id=2; break; } case 'N':{ id=3; break; } case 'E':{ id=4; break; } } //开始发牌 for(int i=0; i<26; i++) { card& c=cards[(i+id)%4][i/4]; scanf("%c%c",&(c.color),&(c.num)); } getchar();//吃回车 for(int i=26; i<52; i++) { card& c=cards[(i+id)%4][i/4]; scanf("%c%c",&(c.color),&(c.num)); } getchar(); for(int i=0; i<4; i++) { sort(&cards[i][0],&cards[i][13]);//卡牌排序 printplayer(i);//按顺时针输出玩家 printCards(cards[i]);//输出排序后的卡牌 } printf("\n"); } }
作业题:
第一题:走迷宫
1.题意简介:输入一个5*5的0/1矩阵,其中0表示可以通过的路径,1表示障碍物(或墙壁),规定左上角为起点,右下角为终点
要求输出从起点到终点的最短路径,输出格式为从起点开始的路径的坐标。
2.做法思路:从题目可以得出,由于迷宫已经规定为5*5的大小,数据处理量不大,因此可以直接用bfs来求解,也可以看出该题目重点考察bfs的运用。
由于bfs可以产生很多条路径,但题目要求的是最短路径,在迷宫起始点已知的情况下,利用bfs寻找到的目前所在点的坐标(x,y)中,若x=4,y=4,则可以确定此时已经到达终点。在寻路过程中用point类型的对象p[25]来记录路径,用int类型的变量j来记录路径长度。
3.代码:
#include<iostream> #include<queue> using namespace std; bool vis[5][5];//记录每个格子经过与否 int a[5][5],dis[5][5]; int sx,sy,tx,ty; int dx[]={0,0,1,-1}; int dy[]={1,-1,0,0}; struct point {//迷宫格 int x,y;//记录格子的坐标 int z; point() {//构造函数 x=-1; y=-1; z=-1; } point(int px,int py) { x=px; y=py; z=-1; } point(int px,int py,int pz) { x=px; y=py; z=pz; } }; point p[25];//记录路径 queue<point> Q; void output(point q) {//输出路径 while(q.z!=-1) { output(p[q.z]); cout<<"("<<q.x<<", "<<q.y<<")"<<endl; return; } } int bfs() {//bfs求最短路径 sx=0,sy=0;tx=4,ty=4; Q.push(point(sx,sx)); vis[sx][sx]=true; dis[sx][sx]=0; int k=0,j=0; p[0]=point(sx,sx); p[0].z=-1; while(!Q.empty()){ point now=Q.front(); Q.pop(); if(now.x==tx&&now.y==ty) { cout<<"(0, 0)"<<endl; output(now); } if(vis[now.x][now.y]==0) continue; for(int i=0;i<4;i++){ int x=now.x+dx[i],y=now.y+dy[i]; if(x>=0&&x<=4&&y>=0&&y<=4&&!vis[x][y]&&a[x][y]!=1){ dis[x][y]=dis[now.x][now.y]+1; vis[x][y]=1; Q.push(point(x,y,j)); k++; p[k]=point(x,y,j); } } j++; } return dis[tx][ty]; } int main() { //创建迷宫 for(int i=0;i<5;i++) for(int j=0;j<5;j++) cin>>a[i][j]; //用bfs寻找路径 bfs(); }
第二题:倒水问题
1.题意简介:开始给定两个容积为A和B的水杯,并指定最终需要两个杯子中留有的水的量C,给定的指令有"fill A" 表示倒满A杯,"empty A"表示倒空A杯,"pour A B" 表示把A的水倒到B杯并且把B杯倒满或A倒空。
要求输出从开始到最后结果过程中对AB两个水杯执行的指令,使得最终A或B两个水杯中有C单位的水。
2.做法分析:从题目中可以看出,该题也可以用bfs进行求解,每一次循环都可以通过用多步if判断A和B中水的含量,对A和B进行倒水操作。通过题目的“A和B互质”可以知道,通过反复对A和B的倒水操作(即进行题干中给出的指令),类似于题目一的迷宫问题,每次循环都执行倒空A,倒空B,倒满A,倒满B等操作,最终一定能得到A或B中有C单位的水的结果,当满足A==C或B==C,就跳出循环,输出指令。
此外,由于要对每一个指令操作进行记录,所以我采用了map<cup,cup>类型的对象m,将每一次操作之后的杯子状态cup1通过m对应到一个cup2中,然后cup2压栈,进行下一个循环的操作,当满足题目要求后,就能运用递归逐级输出每一次循环后的cup中记录的指令。
3.代码:
#include<iostream> #include<queue> #include<map> #include<stdio.h> using namespace std; //申请一组存放操作语句的message数组 string message[7]={"success","empty A","empty B","fill A","fill B","pour A B","pour B A"}; struct cup {//水杯类 int a,b; int mes;//对两个水杯的操作 bool operator<(const cup &s) const {//重载<操作符 return a!=s.a?a<s.a:b<s.b; } }; queue<cup> q;//申请一个杯子类型的队列 map<cup,cup> m; void check(cup c1,cup c2) { if(m.find(c2)==m.end()) { m[c2]=c1; q.push(c2); } } void output(cup p) {//输出倒水接水操作 if(p.a==0&&p.b==0){ return; } output(m[p]); cout<<message[p.mes]<<endl; } void bfs(int a,int b,int c) { //cout<<"1"<<endl; //清空队列 while(!q.empty()) q.pop(); m.clear();//清空map cup c1,c2;//申请杯子 c1.a=0;c1.b=0;c1.mes=0; q.push(c1); //cout<<"1"<<endl; while(!q.empty()) { c1=q.front(); q.pop(); if(c1.a>0) {//A倒空 c2.a=0; c2.b=c1.b; c2.mes=1; check(c1,c2); } if(c1.b>0) {//B倒空 c2.b=0; c2.a=c1.a; c2.mes=2; check(c1,c2); } if(c1.a<a) {//A倒满 c2.a=a; c2.b=c1.b; c2.mes=3; check(c1,c2); if(c1.b!=0){//分情况讨论 if(c1.a+c1.b<=a){ c2.a=c1.a+c1.b; c2.b=0; c2.mes=6; check(c1,c2); } else { c2.a=a; c2.b=c1.a+c1.b-a; c2.mes=6; check(c1,c2); } } } if(c1.b<b) {//B倒满 c2.b=b; c2.a=c1.a; c2.mes=4; check(c1,c2); if(c1.a!=0){//分情况讨论 if(c1.a+c1.b<=b){ c2.b=c1.a+c1.b; c2.a=0; c2.mes=5; check(c1,c2); } else{ c2.b=b; c2.a=c1.a+c1.b-b; c2.mes=5; check(c1,c2); } } } //若a,b杯子都满足需求 if(c1.a==c||c1.b==c) { output(c1); cout<<message[0]<<endl;//输出success return; } } } int main() { //获取杯子容积和需求水量 int a,b,c; while(scanf("%d %d %d",&a,&b,&c)!=EOF) { //用bfs方法求出接水和倒水的过程 bfs(a,b,c); } return 0; }