2.1 Gym - 270437A
2.1.1 关键:判断CH的形式以及特征烃基
2.1.2 题目大意
输入多组,每组5个表征碳原子之间连接方式的数对,判断对应的有机物的种类
input
输入第一行为数据的组数T(1≤T≤200000)。每组数据有5行,每行是两个整数a, b(1≤a,b≤6,a ≤b) 数据保证,输入的烷烃基是给出的5种之一
output
每组数据,输出一行,代表烷烃基的英文名
2.1.3 做法
这个题目关键是找到五个化合物之间的区别
- n-hexane : 4个 -CH2-
- 2-methylpentane: 一个CH, 且有2个 连到了上边
- 3-methylpentane:与2-methylpentane同分异构,实际处理中,只需要判断是否只有一个CH, 并且不符合2-methylpentane,则是3-methylpentane。
- 2,3-dimethylbutane: 两个CH
- 一个C,没有任何H
实际判断依据
在实际编程实现中,首先读入5个数对,然后统计每一个数字(对应的碳原子)连接的数字(碳原子)的个数,找出这个化合物中:C, CH,CH2, CH3的个数,这样就可以判断是不是:2,2-dimethylbutane ,2,3-dimethylbutane,n-hexane。
若不是以上三类,则找到CH对应的碳原子的标号,检查与之相连的碳原子中,是否存在两个CH3,若存在则为3-methylpentane,否则,则为2-methylpentane。
2.1.4代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct attr
{int ch2;int ch;int ch3;int note;int ch3note[10]; attr(){ ch2=ch=ch3=note=0;memset(ch3note,0,sizeof ch3note);}
};
int a[6];
int b[6];
int cnt[10];
void solve()
{memset(cnt,0,sizeof cnt);attr att;for(int i=0;i<5;i++){cnt[a[i]]++;cnt[b[i]]++;}for(int i=1;i<=6;i++){if(cnt[i]==4) {cout<<"2,2-dimethylbutane\n";return; }if(cnt[i]==3){att.ch++;att.note=i;} if(cnt[i]==2) att.ch2++;if(cnt[i]==1) {att.ch3note[att.ch3++]=i;}}if(att.ch==2) {cout<<"2,3-dimethylbutane\n";return; }else if(att.ch2==4) {cout<<"n-hexane\n";return; }else // 一个ch {int ans=0;for(int i=0;i<5;i++){if(a[i]==att.note) {for(int j=0;j<att.ch3;j++){if(att.ch3note[j]==b[i])ans++;}}}for(int i=0;i<5;i++){if(b[i]==att.note) {for(int j=0;j<att.ch3;j++){if(att.ch3note[j]==a[i])ans++;}}}if(ans==2) cout<<"2-methylpentane\n";else cout<<"3-methylpentane\n"; }}
int main()
{int n;cin>>n;for(int i=1;i<=n*5;){if(i%5==1){for(int j=0;j<5;j++,i++) {scanf("%d %d",a+j,b+j);}solve();}memset(a,0,sizeof a);memset(b,0,sizeof b);}return 0;
}
2.2 HDU-2093
2.2.1 关键 : 输入字符串处理,格式输入
2.2.2 题目大意
输入ACM比赛的题目数量m,以及单位罚时m,以及一张ACM比赛的成绩表,人数不固定,读取这张表,并按ACM赛制排名
input:
8 20
GuGuDong 96 -3 40(3) 0 0 1 -8 0
hrz 107 67 -3 0 0 82 0 0
TT 120(3) 30 10(1) -3 0 47 21(2) -2
OMRailgun 0 -99 -8 0 -666 -10086 0 -9999996
yjq -2 37(2) 13 -1 0 113(2) 79(1) -1
Zjm 0 0 57(5) 0 0 99(3) -7 0
output
TT 5 348
yjq 4 342
GuGuDong 3 197
hrz 3 256
Zjm 2 316
OMRailgun 0 0
2.2.3 做法
这道模拟题的思路是比较清晰的,即按照读入字符串,并进行简单的统计,再按要求格式输出。但这个问题难点在于输入输出,尤其是输入还涉及到 是否存在的问题,具体做法分解如下:
输入
因为这个题没有规定输入学生的个数,需要涉及while的使用,而每个字符串一定会包含学生的姓名,所以,将能否读取到学生的名字,作为判断输入是否结束的标志。
此外,最后输出的时候,需要使用到学生的数量,所以需要额外引入cnt变量,来记录输入学生的数量:
所以,第一句while中可以这样写:
while(scanf("%s",stu[cnt].name)!=EOF)
while中的内容主要为:提取每一个题是否作对,以及是否有罚时,由于罚时有()。 具体为:首先读入成绩,若成绩为负则不做处理,此时肯定也没有罚时;若成绩为正,先更新个人的ac题目数量和耗时,之后,检测下一个字符,是否为’(’,若是,则读入罚时,更新个人的罚时,并用getchar()去掉输入中的’)’;若不是(空格),则不需要处理。
int score=0;scanf("%d",&score);if(score < 0) continue;// 如果为负数,说明没有ACif(score > 0){ stu[cnt].acn++;stu[cnt].time+=score;char test;scanf("%c",&test);//这里是关键,用这种方法判断是否有括号if(test=='('){int timeloss=0;scanf("%d",&timeloss);stu[cnt].time+=m*timeloss;getchar();// 吃掉右括号}}}
cnt++;//统计学生数量
这道题AC之后,个人感觉以上做法太麻烦,真正考试调试程序极易出错,经参考他人的做法(参考链接),发现充分利用scanf的性质,就可以解决判断有没有括号的问题
利用下面这句话,只需要判断一下test的值,就可以得知,究竟有没有()
scanf("%d(%d)",&score,&timeloss);
输出
输出由于需要对齐与限制字符宽度,使用了流式输出控制
cout<<setiosflags(ios::left)<<setw(10)<<stu[i].name<<" "<<setiosflags(ios::right) <<setw(2)<<stu[i].acn<<" "<<setw(4)<<stu[i].time<<"\n"<<resetiosflags(ios::right);
2.2.4 代码
//#include<bits/stdc++.h>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<iomanip>
using namespace std;
struct Stu
{char name[20];int acn;int time;Stu(){acn=0;time=0;}bool operator < ( const Stu & a) const{if(acn!=a.acn) return acn>a.acn;if(time!= a.time) return time<a.time;return strcmp(name,a.name)<0 ;}}stu[1000];
int main()
{int n,m,cnt=0;
// freopen("in.txt","r",stdin);scanf("%d%d",&n,&m);while(scanf("%s",stu[cnt].name)!=EOF){
// scanf("%s",stu[cnt].name);for(int i=0;i<n;i++){int score=0;scanf("%d",&score);if(score < 0) continue;if(score > 0){
// cout<<"score:"<<score<<endl;stu[cnt].acn++;stu[cnt].time+=score;char test;scanf("%c",&test);if(test=='('){int timeloss=0;scanf("%d",&timeloss);stu[cnt].time+=m*timeloss;getchar();// )}}}
// cout<<stu[0].name<<" "<<stu[0].acn<<" "<<stu[0].time<<endl;cnt++;}sort(stu,stu+cnt);for(int i=0;i<cnt;i++){cout<<setiosflags(ios::left)<<setw(10)<<stu[i].name<<" "<<setiosflags(ios::right)<<setw(2)<<stu[i].acn<<" "<<setw(4)<<stu[i].time<<"\n"<<resetiosflags(ios::right);}return 0;
}
2.3 POJ-1786
2.3.1 关键:建立合适的映射关系
2.3.2 题目大意
指定一位发牌员(东南西北中的一个,用英文首字母标识)开始发牌,发牌顺序为顺时针,发牌员第一个发他的下一个人(顺时针的下一个人)。这样,每个人都会拿到13张牌。
定义牌的顺序,首先,花色是(梅花)<(方片)<(黑桃)<(红桃),(输入时,用C,D,S,H分别表示梅花,方片,黑桃,红桃,即其单词首字母)。对于牌面的值,规定2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < T < J < Q < K < A。
Input
输入包含多组数据 每组数据的第一行包含一个大写字符,表示发牌员是谁。如果该字符为‘#’则表示输入结束。
接下来有两行,每行有52个字符,表示了26张牌,两行加起来一共52张牌。每张牌都由两个字符组成,第一个字符表示花色,第二个字符表示数值。
output:
输出多组数据发牌的结果,每组数据之后需要额外多输出一个空行!!!!!每组数据应该由24行的组成,输出按照顺时针方向,始终先输出South Player的结果,每位玩家先输出一行即玩家名称(东南西北),接下来五行,第一行和第五行输出固定格式(见样例),第二行和第四行按顺序和格式输出数值(见样例),第三行按顺序和格式输出花色(见样例)。
2.3.3 做法
通过审题,个人认为难点如下:
- 牌花色,数字的大小关系建立(为sort cmp做准备)
- 人名和所摸的牌之间的对应
- 摸牌机制的模拟(循环摸牌)
1. 花色数值映射
- 在刚开始第一遍过这个题的时候,是通过一个映射函数convert完成牌的字母和真实数值之间的映射;通过在Card类中,加入一个变量typecnt,完成花色与大小数值之间的映射(见3.2.4代码);
- 之后感觉非常麻烦,分析发现要建立牌的花色与数值之间,牌的大小与真实的数值之间的一一映射,且映射的两端类型不一样,使用map是最好的选择, 引用十分方便
map<char,int> real_num = { //牌的数值{'1',1}, {'2',2}, ...{'T',10},{'J',11},...
};
map<char,int> color={//牌的花色{'C',0},{'D',1},{'S',2},{'H',3},
}
Card类型中的重载小于就可这样直接用这两个map的数值:
bool operator <(const Card & c) const{return color[type]==color[c.type]?real_num[num]<real_num[c.num]:color[type]<color[c.type];}
2.人名和所摸的牌之间的对应
如果一下子将人名直接映射到牌,应该是map<char*,Card*>,但是由于map按照第一关键字字典序排列,而题目最后要求输出按:South,East…等顺序输出,这样直接用map存储较为麻烦,所以采用间接的方式,用一个map和一个vector结合的方式,map<int,char*> dict存储编号与选手的名称,按输出顺序,South player 是第一个,East player 是第二个… , 之后,vector<char*> v按照dict中的编号顺序,存储,即v[1]是South player 的牌,以此类推。
vector<Card*>v;
map<int,char*> dict;Card cardN[20];Card cardE[20];Card cardS[20];Card cardW[20];//相当于先建立v.push_back(cardS);// 0dict[0]="South";v.push_back(cardW);// 1dict[1]="West";v.push_back(cardN);;// 2dict[2]="North";v.push_back(cardE);// 3dict[3]="East";
3. 摸牌机制
这种轮流摸牌的题,通过: 取余数的方法来模拟
此外这个题因为全部是输入字符,还需要对1,2行之前的换行符进行处理(getchar())
char num, _type;int cnt=0;for(int t=0,f=(start+1)%4;t<52;f=(f+1)%4,t++ ){ if(t%26==0) getchar(); // 吃1,2两行后的换行 scanf("%c%c",&_type,&num);v[f][t/4]=Card(_type,num);}
2.3.4 代码
#include<cstdio>
#include<iostream>
#include<map>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
map<int,char*> dict;
int convert(char c)
{if(c>='2'&&c<='9') return c-'0';else if(c=='T') return 10;else if(c=='J') return 11;else if(c=='Q') return 12;else if(c=='K') return 13;else if(c=='A') return 14;
}
struct Card
{char type;char num;int real_num;int typecnt;Card(char _type,char _n,int _cnt){type=_type;num=_n;real_num=convert(_n);typecnt=_cnt;}Card() {type=num=0;}bool operator <(const Card & c) const{return typecnt==c.typecnt?real_num<c.real_num:typecnt<c.typecnt;}
};
vector<Card*>v;
int main()
{char c;
// freopen("in.txt","r",stdin);Card cardN[20];Card cardE[20];Card cardS[20];Card cardW[20];v.push_back(cardS);// 0dict[0]="South";v.push_back(cardW);// 1dict[1]="West";v.push_back(cardN);;// 2dict[2]="North";v.push_back(cardE);// 3dict[3]="East";while(1){scanf("%c",&c);
// cout<<c<<endl;if(c=='#') break;int start=0;for(int i=0;i<4;i++){if(dict[i][0]==c) //map->second{start=i;break;}}char num, _type;int cnt=0;for(int t=0,f=(start+1)%4;t<52;f=(f+1)%4,t++ ){ if(t%26==0) getchar(); // 吃掉换行 scanf("%c%c",&_type,&num);if(_type=='C') cnt=0;if(_type=='D') cnt=1;if(_type=='S') cnt=2;if(_type=='H') cnt=3;
// cout<<dict[f]<<": ";v[f][t/4]=Card(_type,num,cnt);
// cout<<v[f][t/4].num<<" "<<v[f][t/4].type<<endl;}sort(cardS,cardS+13);sort(cardN,cardN+13);sort(cardW,cardW+13);sort(cardE,cardE+13);for(int p=0;p<4;p++){printf("%s player:\n",dict[p]);printf("+---+---+---+---+---+---+---+---+---+---+---+---+---+\n");for(int k=0;k<13;k++){if(k==0) printf("|");printf("%c %c|",(v[p]+k)->num,(v[p]+k)->num);}printf("\n");for(int k=0;k<13;k++){if(k==0) printf("|");printf(" %c |",(v[p]+k)->type);}printf("\n");for(int k=0;k<13;k++){if(k==0) printf("|");printf("%c %c|",(v[p]+k)->num,(v[p]+k)->num);}printf("\n");printf("+---+---+---+---+---+---+---+---+---+---+---+---+---+\n");}printf("\n");getchar();// 吃掉多余的\n
}return 0;
}