A - 化学 【Gym - 270437A】暴力求解
题意:
这个烷烃基有6个原子和5个化学键,6个原子分别标号1~6,然后用一对数字 a,b 表示原子a和原子b间有一个化学键。这样通过5行a,b可以描述一个烷烃基。
任务是甄别烷烃基的类别。
思路:
依据图意可知:这是个六原子烷烃基的同分异构体,关键是如何区分。首先根据碳原子所连接的最大原子个数来区分:这样可以分为三类 n-hexane 原子连接的最大原子个数为2;2-methylpentane、3-methylpantane、2,3-dimethybutane最大原子个数为3;2,2-dumethylbutane最大原子个数为4。这样就确定了两个,再对中间三个不同类型找出区别。会发现2,3-dimethybutane有两个原子都连接三个原子,其他的只有一个原子连接,2,3确定。2-methylpentane和3-methylpantane最大的区别就是3-methylpantane最大原子连接个数的原子连接的原子当中只有一个它的连接原子个数为1,而2-methylpentane为2。就此可以区分所有的同分异构体。
有了思路:可以选用vector或者queue来存储数据,在这里选用的是vector二维向量来表示 vector to[0-6]存储从数1 - 6 相连接的点。maxsize找到从to[0-6].size()中最大的那个,maxsize=2 是n-hexane,maxsize=4是2,2-dumethylbutane;再利用count计算maxsize()==to[0-6].size()的个数,个数为2的是2,3-dimethybutane;最后再利用dig计算to[1-6].size()最大的那个原子中所有相邻的原子的size()=1的进行计数,dig=1的为2-methylpentane,dig的=2为3-methylpantane。
总结:
这道题主要是观察不同物质的特点,可以暴力求解每个原子的边数,利用原子最多边数为多少来第一次区分。当然会分辨不全的情况,这时候就应该寻找其他的特点,比如最多边数的原子最多有多少个,再或者原子相邻原子边数为某某的个数。此外做本题的时候应该加深对queue和vector的stl的使用,这样能提高做题的效率。
代码:
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
vector<int> to[7];
void insert1(int a, int b)
{
to[a].push_back(b);
}
void name()
{
int maxsize=0;
int count=0;
int dig = 0;
for (int i = 1; i < 7; i++)
if (to[i].size() > maxsize)
maxsize = to[i].size();
if (maxsize == 2)
{
cout << "n-hexane" << endl;
return;
}
else if (maxsize == 3)
{
for (int j = 1; j < 7; j++)
{
if (to[j].size() == maxsize)
count++;
}
if (count == 1)
{
int k;
for (k = 1; k < 7; k++)
{
if (to[k].size() == maxsize)
break;
}
for (int q = 0; q <= to[k].size()-1; q++)
{
if (to[to[k][q]].size() == 1)
dig++;
}
if (dig == 2)
{
cout << "2-methylpentane" << endl;
return;
}
else if (dig == 1)
{
cout << "3-methylpentane" << endl;
return;
}
}
else if (count == 2)
{
cout << "2,3-dimethylbutane" << endl;
return;
}
}
else if (maxsize == 4)
{
cout << "2,2-dimethylbutane" << endl;
return;
}
}
int main()
{
int T;
int a, b;
void name();
cin >> T;
for (int i = 0; i < T; i++)
{
for (int j = 0; j< 5; j++)
{
cin >> a >> b;
insert1(a, b);
insert1(b, a);
}
name();
for (int j = 1;j < 7; j++)
{
to[j].clear();
}
}
}
B - 爆零(×)大力出奇迹【HDU - 2093 】
题意:
某次考试一共n道题,每名同学有提交AC的和提交错误的,AC的会显示所耗时间,同时括号内显示错误提交了多少次,错误的会显示提交错误的次数(显示负数),如果未提交则为0。目标是通过一组数据(例如:GuGuDong 96 -3 40(3) 0 0 1 -8 0),求得该名同学AC的题目和对所用时间分,并先根据AC题数,AC题数相等后再根据所用时间分,前两种情况都相等再根据字典序进行排序输出。
思路:
首先我们先考虑输出,要构建一个student的结构体(包含姓名,AC题数和时间分)并初始化。初始化之后开始观察每一行的数据分析,每一道题要么有两个数,要么有一个数,一个数的可正可负;这个时候就可以想到通过分开计数并提取出这些数据的方式,并通过原来的学习知道了 sscanf 的使用方法。这样就可以每输入一个学生的姓名后进行n次以下循环,利用count=sscanf(a,"%d(%d)",&b,&c)。如果计数为2的情况就代表该题AC并且加和时间分;如果计数为1的情况并且该数大于0代表该题AC并且加和计算分,完成循环后再进行排序操作即可完成所需要求。
总结:
重点1:
这道题可以往字符串提取的方面去想,及判断这些字符串能否用整型提取。提取可以选择%d(%d),如果里面的内容均符合,则把里面的整型数据全部提取,如果只有部分符合,则提取符合的部分。
重点2:
选取良好的sort方式可以使代码简洁方便,以下程序的注释代码是我后来学习过程中新增了一些,利用bool类型cmp函数(const 数据类型&,const 数据类型&)返回并依次排序,会使排序更加的有效率。
代码:
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
struct student
{
string name;
int ac;
int time;
}s[1000];
/* bool cmp(const student& a,const student& b)
{
if(a.AC!=b.AC) return a.AC > b.AC;
else if(a.time!=b.time) return a.time < b.time;
else return a.name < b.name;
}*/
void sorting(int i)
{
for(int k=0;k<i-1;k++)
{
for(int j=k+1;j<i;j++)
{
if(s[k].ac<s[j].ac)
{
swap(s[k].name,s[j].name);
swap(s[k].ac,s[j].ac);
swap(s[k].time,s[j].time);
}
}
}
for(int j=0;j<i-1;j++)
{
if(s[j].ac==s[j+1].ac)
{
if(s[j].time>s[j+1].time)
{
swap(s[j].name,s[j+1].name);
swap(s[j].ac,s[j+1].ac);
swap(s[j].time,s[j+1].time);
}
}
}
for(int j=0;j<i-1;j++)
{
if(s[j].ac==s[j+1].ac&&s[j].time==s[j+1].time)
{
if(strcmp(s[j].name,s[j+1].name)>0)
{
swap(s[j].name,s[j+1].name);
swap(s[j].ac,s[j+1].ac);
swap(s[j].time,s[j+1].time);
}
}
}
for(int k=0;k<i;k++)
{
printf("%-10s %2d %4d\n",s[k].name,s[k].ac,s[k].time);
}
}
int main()
{
int n, m,i=0;
int b,c,count;
char a[15];
void sorting(int i);
scanf("%d %d",&n,&m);
for(i=0;~scanf("%10s",&s[i].name);i++)
{
s[i].ac=0;
s[i].time=0;
for(int j=0;j<n;j++)
{
scanf("%s",&a);
count=sscanf(a,"%d(%d)",&b,&c);
if(count==2)
{
s[i].ac++;
s[i].time =s[i].time + b + c * m;
}
else if(count==1 && b>0)
{
s[i].ac++;
s[i].time =s[i].time + b;
}
}
}
/*
sort(a,a+i,cmp);
for(int k=0;k<i;k++)
printf("%-10s %2d %4d\n",a[k].name,c[k].AC,a[k].time);
*/
return 0;
}
C - 瑞神打牌
题意:
1.先输入一个字母,代表发牌员是谁。
2.发牌员将52张牌顺时针依次发给所有人(包括自己)并先发给下一个人。
3.再输入两个数据其中每行数据52个字符并且奇数字符代表牌的花色,偶数字符代表牌面值。每张牌由两个字符组成。
4.始终先输出south player所含所有牌依次排序的结果,再按顺时针依次输出其余三人所含所有牌依次排序的结果(需按照指定格式)。
5.对于排序方式:首先,花色是(梅花)<(方片)<(黑桃)<(红桃),(输入时,我们用C,D,S,H分别表示梅花,方片,黑桃,红桃)。其次规定牌面的值2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < T < J < Q < K < A。
6.直至遇到#结束
思路:
这道题主要是分辨出牌属于谁手中,然后将牌花色和面值保存为属于的发牌员,这时候可以定义一个结构体然后以vector的形式保存。每次将奇数字符和偶数字符利用vector插入,所有的字符判断完后就确定了每个玩家的所有牌,接着将依次将每个玩家的所有牌按照花色牌面值排序。排序完就可以从south player开始顺时针将向量存储的数据按照格式输出即可。
代码`
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
struct people //a代表花色,b代表牌号
{
char a, b;
people();
people(char a, char b) :a(a), b(b) {};
};
vector<people> v[4];
int m; //m为循环判断
char n, s[500];
int judgecolor(char n) //花色判断
{
switch (n)
{
case('C'):
m = 0;
break;
case('D'):
m = 1;
break;
case('S'):
m = 2;
break;
case('H'):
m = 3;
break;
}
return m;
}
int judgepeople(char n) //发牌员判断
{
switch (n)
{
case('E'):
return 0;
case('S'):
return 1;
case('W'):
return 2;
case('N'):
return 3;
}
}
int compare(char s)
{
switch (s)
{
case('T'):
return 10;
case('J'):
return 11;
case('Q'):
return 12;
case('K'):
return 13;
case('A'):
return 14;
}
return s - '0';
}
bool cmp(const people& x, const people& y)
{
if (x.a == y.a)
return compare(x.b) < compare(y.b);
return judgecolor(x.a) < judgecolor(y.a);
}
int main()
{
while (cin >> n)
{
if (n == '#') break;
m=judgepeople(n);
for (int i = 0; i < 4; i++)
v[i].clear();
for (int i = 0; i < 52; i++)
cin >> s[i];
for (int i = 52; i < 104; i++)
cin >> s[i];
for (int i = 0; i < 104; i = i + 2, m = (m + 1) % 4)
v[m].push_back(people(s[i], s[i + 1]));
for (int i = 0; i < 4; i++)
sort(v[i].begin(), v[i].end(), cmp);
for (int i = 0; i < 4; i++)
{
switch (i)
{
case 0:
cout << "South player:"<< endl;
break;
case 1:
cout<<"West player:" << endl;
break;
case 2:
cout << "North player:" << endl;
break;
case 3:
cout << "East player:" << endl;
break;
}
cout << "+---+---+---+---+---+---+---+---+---+---+---+---+---+" << endl;
for (int j = 0; j < v[i].size(); j++)
{
cout << "|" << v[i][j].b << " " << v[i][j].b;
if (j == v[i].size() - 1)
cout << "|" << endl;
}
for (int j = 0; j < v[i].size(); j++)
{
cout << "| " << v[i][j].a << " ";
if (j == v[i].size() - 1)
cout << "|" << endl;
}
for (int j = 0; j < v[i].size(); j++)
{
cout << "|" << v[i][j].b << " " << v[i][j].b;
if (j == v[i].size() - 1)
cout << "|" << endl;
}
cout << "+---+---+---+---+---+---+---+---+---+---+---+---+---+" << endl;
}
cout << endl;
}
return 0;
}
作业A - Maze
题意:
这是一个走迷宫的问题,可以走的路和障碍物分别用0-1代替。左上角为入口,右下角为出口,要求输出从出口到入口的最短路径并输出所有经过的所有位置。
思路:
这个题可以用广度优先搜索来实现,实现算法大致如下:起始点(0,0),重点为(4,4),用一个bool类型数组来标记已经过的点并初始化一开始所有的点。当bool类型数组=true时,则证明已经访问过;接下来就可以利用广度来一步一步探索:
将第一个节点显示已经访问,并放入队列q中,取出队列q的第一个节点并取出相邻的节点判断是否到达(4,4),如果没有则将相邻的节点压入队列中。再取出头一个节点,以此类推,直到取出的相邻节点到达(4,4),此时就获得了最终结果。
由以上的探索过程:我们就可以知道要用结构体来表示坐标并初始化,要有四个方向,由方向来寻找相邻坐标,将起始点压入队列中并计算相邻的节点,如果到达则退出,如果没有到达判断该相邻节点是否合法(非合法:已经访问或者为障碍物),合法就将其压入队列中,重复以上过程就可以找到出口。在此过程中利用distant来计算出最短距离,另外一个数组计算出经过的所有点到入口的距离,由此可以输出我们想要的坐标。
代码:
#include <iostream>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
struct point{
int x,y;
point(int a=0,int b=0):x(a),y(b){}
};
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
int a[5][5];
bool v[5][5];
int b[5][5];
int main()
{
int distant=0;
for (int i=0; i<5; i++)
for(int j=0;j<5;j++)
cin>>a[i][j];
queue<point> q;
q.push(point(0,0));
v[0][0]=true;
while (!q.empty())
{
point p=q.front();
q.pop();
if(p.x==4&&p.y==4)
{distant=b[p.x][p.y];break;}
for (int i=0; i<4; i++) {
int m=p.x+dx[i];
int n=p.y+dy[i];
if(!v[m][n]&&a[m][n]==0&&m>=0&&n>=0&&m<=4&&n<=4)
{
v[m][n]=true;
b[m][n]=b[p.x][p.y]+1;
q.push(point(m,n));
}
}
}
vector<point> vc;//输出最短路径也可以用stack来实现
vc.push_back(point(4,4));
distant--;
while (distant>0)
{
point p=vc.back();
for (int i=0; i<4; i++) {
int m=p.x+dx[i];
int n=p.y+dy[i];
if(b[m][n]==distant)
{
vc.push_back(point(m,n));
}
}
distant--;
}
cout<<"("<<0<<", "<<0<<")"<<endl;
while (!vc.empty())
{
cout<<"("<<vc.back().x<<", "<<vc.back().y<<")"<<endl;
vc.pop_back();
}
return 0;
}
B - Pour Water
题意:
A B C分别代表杯子A的最满量,B代表B的最满量,导致任何一个罐子正好包含C单位的水。fill A,empty A,fill B,empty B,pour A B,pour B A。分别代表将A灌满,将倒完,将B灌满和倒完,将A倒给B,将B倒给A。输出完成这些操作的一系列步骤。
思路:
这个题的思想和前面迷宫类似,题意就是从状态中的A B其中一个达到目标状态下的C,且每一个状态的A B是不能重复的,这时候我们就可以建立一个结构体state,并且重载<以便对结构体排序。state除了AB之外还增添了命令每个命令0-6可以为后来的递归输出提供方便。此外利用map映射确定当前状态是不是已经被访问,没访问则放入队列。并建立state temp和state demo前者是当前取出的节点进行判断,来进行fill A-pour B A等一系列操作,而后者则是将完成操作后的状态插入队列中。遍历过程和bfs核心遍历操作类似,最后的存储可以用queue(stack vector)来完成。输出可以利用output函数递归来进行输出。另外需格外注意每次循环前要清空映射map所保存的数据。
总结:
其实这是典型的BFS应用,和走迷宫类似,都是通过取出节点,判断节点(当前状态是否符合),找到相邻节点(此时为可以理解为可以发生的状态),判断相邻节点是否已经被访问(已到达的状态)如果没有访问,就进行存储,循环直到达到目标即可。
代码:
#include<iostream>
#include<string>
#include<queue>
#include<map>
using namespace std;
struct state
{
int a, b;
int order;
bool operator<(const state& p) const
{
if (a != p.a) return a < p.a;
return b < p.b;
}
state()
{
a = -1; b = -1; order = -1;
}
state(int aa, int bb)
{
a = aa; b = bb; order = -1;
}
state(const state& p)
{
a = p.a; b = p.b; order = p.order;
}
state(int aa, int bb, int oo)
{
a = aa;
b = bb;
order = oo;
}
};
string orders[6] = { "fill A","empty A","fill B","empty B","pour A B","pour B A" };
map<state, state> front;
void output(state conse)
{
if (conse.a == 0 && conse.b == 0)
return;
output(front[conse]);
cout << orders[conse.order] << endl;
}
void bfs_pour(int a, int b, int c)
{
queue<state> q;
q.push(state(0, 0, -1));
state temp(-1, -1, -1);
state demo(-1, -1, -1);
while (!q.empty())
{
temp.a = -1; temp.b = -1; demo.a = -1; demo.b = -1;
temp = q.front();
q.pop();
if (temp.a == c || temp.b == c)
{
output(temp);
cout << "success" << endl;
return;
}
//fill A
if (temp.a != a)//先判断再决定,否则这个分支就是重复的
{
demo.a = a; demo.b = temp.b; demo.order = 0;
if (front.find(state(demo.a, demo.b, 0)) == front.end())//是否到达过这个state ,没到达再放入队列,否则队列就有很多重复的
{
q.push(state(demo.a, demo.b, 0));//如果不创建临时变量,就会堆溢出
front[state(demo.a, temp.b, 0)] = temp;
}
}
//empty A
if (temp.a != 0)
{
demo.a = 0; demo.b = temp.b; demo.order = 1;
if (front.find(state(a, demo.b, 1)) == front.end())
{
q.push(state(demo.a, demo.b, 1));
front[state(a, demo.b, 1)] = temp;
}
}
//fill B
if (temp.b != b)
{
demo.a = temp.a; demo.b = b; demo.order = 2;
if (front.find(state(demo.a, demo.b, 2)) == front.end())
{
q.push(state(demo.a, demo.b, 2));
front[state(demo.a, demo.b, 2)] = temp;
}
}
//empty B
if (temp.b != 0)
{
demo.a = temp.a; demo.b = 0; demo.order = 3;
if (front.find(state(demo.a, demo.b, 3)) == front.end())
{
q.push(state(demo.a, demo.b, 3));
front[state(demo.a, demo.b, 3)] = temp;
}
}
//pour A B
if (temp.a >= (b - temp.b))//把b倒满
{
demo.a = a - (b - temp.b); demo.b = b; demo.order = 4;
}
else//把a倒空
{
demo.a = 0; demo.b = temp.b + temp.a; demo.order = 4;
}
if (front.find(state(demo.a, demo.b, 4)) == front.end())
{
q.push(state(demo.a, demo.b, 4));
front[state(demo.a, demo.b, 4)] = temp;
}
//pour B A
if (temp.b >= (a - temp.a))//把a倒满
{
demo.a = a; demo.b = temp.b - (a - temp.a); demo.order = 5;
}
else//把b倒空
{
demo.a = temp.a + temp.b; demo.b = 0; demo.order = 5;
}
if (front.find(state(demo.a, demo.b, 5)) == front.end())
{
q.push(state(demo.a, demo.b, 5));
front[state(demo.a, demo.b, 5)] = temp;
}
}
return;
}
int main()
{
int asize = 0; int bsize = 0; int csize = 0;//初始化
state consequence(0, 0, -1);
while (cin >> asize >> bsize >> csize)
{
if (csize == 0)
{
cout << "empty A" << endl;
cout << "success" << endl;
continue;
}
front.clear();
bfs_pour(asize, bsize, csize);
}
return 0;
}