模拟1

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 做法

这个题目关键是找到五个化合物之间的区别

  1. n-hexane : 4个 -CH2-
  2. 2-methylpentane: 一个CH, 且有2个 C H 3 CH_3 CH3连到了上边
  3. 3-methylpentane:与2-methylpentane同分异构,实际处理中,只需要判断是否只有一个CH, 并且不符合2-methylpentane,则是3-methylpentane。
  4. 2,3-dimethylbutane: 两个CH
  5. 一个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;
      // 如果为负数,说明没有AC
   if(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 做法

通过审题,个人认为难点如下:

  1. 牌花色,数字的大小关系建立(为sort cmp做准备)
  2. 人名和所摸的牌之间的对应
  3. 摸牌机制的模拟(循环摸牌)
1. 花色数值映射
  1. 在刚开始第一遍过这个题的时候,是通过一个映射函数convert完成牌的字母和真实数值之间的映射;通过在Card类中,加入一个变量typecnt,完成花色与大小数值之间的映射(见3.2.4代码);
  2. 之后感觉非常麻烦,分析发现要建立牌的花色与数值之间,牌的大小与真实的数值之间的一一映射,且映射的两端类型不一样,使用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);// 0
 dict[0]="South";
 v.push_back(cardW);// 1
 dict[1]="West";
 v.push_back(cardN);;// 2
 dict[2]="North";
 v.push_back(cardE);// 3
    dict[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);// 0
	dict[0]="South";
	v.push_back(cardW);// 1
	dict[1]="West";
	v.push_back(cardN);;// 2
	dict[2]="North";
	v.push_back(cardE);// 3
    dict[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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值