记录week2的实验三道题+作业两道题
迷宫老鼠 倒水问题 化学式 多关键字排序 扑克
作业:
一:迷宫老鼠:
Input:
输入是一个5 × 5的二维数组,仅由0、1两数字组成,表示法阵地图。
Output
输出若干行,表示从左上角到右下角的最短路径依次经过的坐标,格式如样例所示。数据保证有唯一解。
这道题在课堂上讲过很多次,但是一直没动手写,思路大概就是:初始化迷宫,设置固定的四种移动方式,利用bfs,移动的时候将经过的地方标记并且压栈;用栈储存经过的路线的坐标,当无路可走时就从栈里取元素恢复路线直到有路可走
思路还是比叫容易理解,但是写的时候很多细节容易出错。
代码如下:
//#include<bits/stdc++.h>
#include<iostream>
#include<stack>
using namespace std;
struct position{
int x,y;
};
int _map[7][7];
position offset[4];
void pre_map_offset()//初始化 地图 + 位移
{
for(int i=1;i<6;i++)
for(int j=1;j<6;j++)
cin>>_map[i][j];
for(int i=0;i<7;i++){_map[i][0]=1;_map[0][i]=1;_map[6][i]=1;_map[i][6]=1;}
offset[0].x=1;offset[0].y=0;//右
offset[3].x=-1;offset[3].y=0;//左
offset[2].x=0;offset[2].y=1;//上
offset[1].x=0;offset[1].y=-1;//下
}
void check()
{
for(int i=0;i<7;i++)
{
for(int j=0;j<7;j++)
cout<<_map[i][j]<<" ";
cout<<endl;
}
for(int i=0;i<4;i++)
cout<<offset[i].x<<offset[i].y<<endl;
}
void bfs(int sx,int sy,int dx,int dy)
{
stack<position>s;int nowi=0;
position now;now.x=sx;now.y=sy;
s.push(now);_map[now.x][now.y]=1;
//cout<<s.top().x<<endl;
int count=10000;
while(now.x!=dx||now.y!=dy)
{ //cout<<s.top().x<<" "<<s.top().y<<endl;
//check();
count--;if(count<0){cout<<" false from while";break;}
now.x=s.top().x;//从栈顶取元素
now.y=s.top().y;
for(nowi=0;nowi<4;nowi++)
{
if(_map[now.x+offset[nowi].x][now.y+offset[nowi].y]==0)
{ _map[now.x][now.y]=1;//若找到了可走区域 置为一
now.x=now.x+offset[nowi].x;
now.y=now.y+offset[nowi].y; break;}
}
if(nowi<=3){//将当前位置压栈
s.push(now);
// cout<<"hhhhhh"<<endl;
}
else{
if(s.empty()){
cout<<"error:stack empty! "<<endl; return ;
}
//无位置 从栈顶取元素
_map[now.x][now.y]=1;
s.pop();
}
}
if(now.x==dx&&now.y==dy)
;
else cout<<"false!"<<endl;
stack<position> rs;
while(!s.empty())
{
position t;t.x=s.top().x-1; t.y=s.top().y-1;
//cout<<t.x<<" "<<t.y<<endl;
s.pop();
rs.push(t);
}
while(!rs.empty())
{
cout<<"("<<rs.top().x<<", "<<rs.top().y<<")"<<endl;
rs.pop();
}
}
int main()
{
pre_map_offset();
//check();
bfs(1,1,5,5);
return 0;
}
倒水问题:
也是一道见过很多次但是没深入思考的题目,感觉跟互质数的取余有关,课上讲时是找出固定的状态变化,然后用bfs一直找符合有C的水的状态
描述:
“fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。
Input
输入包含多组数据。每组数据输入 A, B, C 数据范围 0 < A <= B 、C <= B <=1000 、A和B互质。
Output
你的程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含C单位的水。每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。
这道题我觉得对我这种新手来说还是挺难的;他难就难在我想不到这么做,联系不到bfs上,但是做完就觉得思路还是很合理的:
状态变化一共八种分别列举出来,初状态入队,重复将队首取出分别计算下一个状态并入队,直至找到合适的状态,在这里将状态用结构体表示,为了最后依序输出,这里为每个节点加上了它的前一个状态的坐标节点px 和 py,以flag表示是否遍历过,遍历过的不再入队。建立个足够大的结构体矩阵,用来存放杯子里水的不同状态。找到最终节点后就可以利用px py在图内遍历路径。
#include<iostream>
#include<queue>
using namespace std;
int a,b,c;
struct node{
int x,y;
int op;
int px,py;
int flag;
};
int path[100000]={0};
node g[1005][1005];
void pre_g(){
for(int i=0;i<1005;i++)
{
for(int j=0;j<1005;j++)
{
g[i][j].flag=-1;
g[i][j].op=-1;
g[i][j].x=0;g[i][j].y=0;
g[i][j].px=-1;g[i][j].py=-1;
}
}
}
int lastx,lasty,lastop;
int bfs()
{
queue<node>q;
node now;now.op=-2 ;now.px=-1;now.py=-1;now.x=0;now.y=0;
node next;
q.push(now);now.flag=1;
g[now.x][now.y].flag=-1; g[now.x][now.y].op=-2; g[now.x][now.y].px=-1; g[now.x][now.y].py=-1; g[now.x][now.y].x=0; g[now.x][now.y].y=0;
while(!q.empty())
{
//cout<<"inwhile"<<endl;
now.flag=q.front().flag;
now.x=q.front().x;
now.y=q.front().y;
now.op=q.front().op;
now.px=q.front().px;
now.py=q.front().py;
for(int i=0;i<8;i++)//
{
if(i==0)
{
next.x=a;
next.y=now.y;
next.op=0;
}
else if(i==1)
{
next.x=now.x;
next.y=b;
next.op=1;
}
else if(i==2)
{
next.x=0;
next.y=now.y;
next.op=2;
}
else if(i==3)
{
next.x=now.x;
next.y=0;
next.op=3;
}
else if(i==4&&(now.x+now.y)<=b) //将a倒入b 1、a空y=y+x;x=0;
{
next.x=0;
next.y=now.y+now.x;
next.op=4;
}
else if(i==5&&(now.x+now.y)>b)// 2、a余下 b满 x=x+y-b;y=b;
{
next.x=now.x+now.y-b;
next.y=b;
next.op=5;
}
else if(i==6&&(now.x+now.y)<=a)//将b倒入a 1、b空
{
next.x=now.x+now.y;
next.y=0;
next.op=6;
}
else if(i==7&&(now.x+now.y)>a)//2、b余下 a满
{
next.x=a;
next.y=now.y-(a-now.x);
next.op=7;
}
else{
continue;
}
if(g[next.x][next.y].flag==-1&&next.x!=c&&next.y!=c){//若下个状态未经历过 并且不为目标状态
//cout<<i<<" "<<now.x<<" "<<now.y<<"->"<<next.x<<" "<<next.y<<endl;
next.px=now.x;next.py=now.y;next.flag=1; //将下个状态的前置状态置为当前状态
q.push(next); //下个状态入队
g[next.x][next.y].x=next.x;g[next.x][next.y].y=next.y;//在状态图里将 下个状态 对应 节点更新
g[next.x][next.y].op=next.op;
g[next.x][next.y].px=now.x;g[next.x][next.y].py=now.y;
g[next.x][next.y].flag=1;
}
if(next.x==c||next.y==c)//若找到节点
{ //cout<<i<<" "<<now.x<<" "<<now.y<<"->"<<next.x<<" "<<next.y<<endl;
next.px=now.x;next.py=now.y;next.flag=1;
g[next.x][next.y].x=next.x;g[next.x][next.y].y=next.y;
g[next.x][next.y].op=next.op;
g[next.x][next.y].px=now.x;g[next.x][next.y].py=now.y;
g[next.x][next.y].flag=1;
lastx=now.x;lasty=now.y;lastop=i; int u=lastop;
int temp_x=lastx;int temp_y=lasty;int co=0;
do
{
if(u==0){
path[co]=0;
}
if(u==1){
path[co]=1;
}
if(u==2){
path[co]=2;
}
if(u==3){
path[co]=3;
}
if(u==4||u==5){
path[co]=4;
}
if(u==6||u==7){
path[co]=5;
}co++;
//cout<<"flag:"<<g[lastx][lasty].flag<<" op:"<<g[lastx][lasty].op<<" px:"<<g[lastx][lasty].px<<" py:"<<g[lastx][lasty].py<<" x:"<<g[lastx][lasty].x<<" y:"<<g[lastx][lasty].y<<endl;
u=g[lastx][lasty].op;
temp_x=lastx;temp_y=lasty;
lastx=g[temp_x][temp_y].px;
lasty=g[temp_x][temp_y].py;
}while(g[temp_x][temp_y].x!=0||g[temp_x][temp_y].y!=0);
for(int k=co-1;k>=0;k--){
// cout<<path[k]<<endl;
if(path[k]==0){
cout<<"fill A"<<endl;
}
if(path[k]==1){
cout<<"fill B"<<endl;
}
if(path[k]==2){
cout<<"empty A"<<endl;
}
if(path[k]==3){
cout<<"empty B"<<endl;
}
if(path[k]==4){
cout<<"pour A B"<<endl;
}
if(path[k]==5){
cout<<"pour B A"<<endl;
}
}
return 1;
}
}
q.pop();
}
}
int main()
{
while(scanf("%d%d%d",&a,&b,&c)!=EOF)
{
pre_g();
bfs();
cout<<"success"<<endl;
}
return 0;
}
实验:
化学:
假设如上图,这个烷烃基有6个原子和5个化学键,6个原子分别标号1~6,然后用一对数字 a,b 表示原子a和原子b间有一个化学键。这样通过5行a,b可以描述一个烷烃基
你的任务是甄别烷烃基的类别。
原子没有编号方法,比如
1 2
2 3
3 4
4 5
5 6
和
1 3
2 3
2 4
4 5
5 6
是同一种
Input
输入第一行为数据的组数T(1≤T≤200000)。每组数据有5行,每行是两个整数a, b(1≤a,b≤6,a ≤b)
数据保证,输入的烷烃基是以上5种之一
Output
每组数据,输出一行,代表烷烃基的英文名
简而言之分辨这五种化学分子,这道题实验课之前没接触过,乍一看毫无思路,下午回过神来又看了看这个图突然发现了什么了不得的事情,第一个分子各个原子最大的连接其他原子数是2,第二三四个是3,第四个是4,但是第四个有两个为3,第三个与为3的原子相连的有两个为1的,第二个则有两个为2的,这样我们用图表示每个输入的分子,统计他们的度数即可。但是这道题还是差点令人爆炸,因为某些不明显的错误。
//#include<bits/stdc++.h>
#include<iostream>
using namespace std;
//1 0 0 0 1 0 0
//1 0 0 1 0 0 0
//2 0 1 0 1 0 0
//3 1 0 1 0 1 0
//2 0 0 0 1 0 1
//1 0 0 0 0 1 0
int a[7][7];
void out(){
for(int i=0;i<7;i++)
{for(int j=0;j<7;j++)
{
cout<<a[i][j]<<" ";
} cout<<endl;
}
}
int main(){
int T;cin>>T;
for(int i=0;i<T;i++)
{
for(int ci=0;ci<7;ci++)for(int cj=0;cj<7;cj++)a[ci][cj]=0;
int x=0;int y=0;
int maxd=0;int maxcount=0;
int maxpx=0;
for(int j=0;j<5;j++)
{
scanf("%d%d",&x,&y);
if(x!=y&&a[x][y]!=1){
a[x][y]=1;a[x][0]+=1;
a[y][x]=1;a[y][0]+=1;
}
} maxd=a[1][0];maxpx=1;
for(int k=2;k<7;k++)
{
if(maxd<a[k][0])
{maxpx=k;
maxd=a[k][0];
}
}
// cout<<maxpx<<endl;
for(int kk=1;kk<7;kk++)
{
if(a[kk][0]==maxd)
maxcount++;
}
//out();
if(maxd==2)
cout<<"n-hexane"<<endl;
if(maxd==4)
printf("2,2-dimethylbutane\n");
if(maxd==3)
{
if(maxcount==2)
cout<<"2,3-dimethylbutane"<<endl;
else if(maxcount==1)
{
int sec_count=0;int sec=0;
for(int jj=1;jj<7;jj++)
{
if(a[maxpx][jj]>0)
{
if(a[jj][0]>sec)
sec=a[jj][0];
}
}
for(int jj=1;jj<7;jj++)
{//cout<<a[maxpx][jj]<<" ";
if(a[maxpx][jj]>0)
{
if(a[jj][0]==sec)
sec_count++;
}
}
if(sec_count==2)
{
cout<<"3-methylpentane"<<endl;
}
else if(sec_count==1)
{
cout<<"2-methylpentane"<<endl;
}
else cout<<"maxpx"<<endl;
//cout<<sec<<" "<<sec_count<<endl;
}
else cout<<"error in maxd=3"<<endl;
}
}
return 0;
}
成绩
描述:
例如某次考试一共八道题(A,B,C,D,E,F,G,H),每个人做的题都在对应的题号下有个数量标记,负数表示该学生在该题上有过的错误提交次数但到现在还没有AC,正数表示AC所耗的时间,如果正数a跟上了一对括号,里面有个正数b,则表示该学生AC了这道题,耗去了时间a,同时曾经错误提交了b次。例子可见下方的样例输入与输出部分。
Input
输入数据包含多行,第一行是共有的题数n(1≤n≤12)以及单位罚时m(10≤m≤20),之后的每行数据描述一个学生的信息,首先是学生的用户名(不多于10个字符的字串)其次是所有n道题的得分现状,其描述采用问题描述中的数量标记的格式,见上面的表格。
Output
根据这些学生的得分现状,输出一个实时排名。实时排名显然先按AC题数的多少排,多的在前,再按时间分的多少排,少的在前,如果凑巧前两者都相等,则按名字的字典序排,小的在前。每个学生占一行,输出名字(10个字符宽),做出的题数(2个字符宽,右对齐)和时间分(4个字符宽,右对齐)。名字、题数和时间分相互之间有一个空格。数据保证可按要求的输出格式进行输出。
这道题思路相当明显了,跟第一周的多关键字排序类似,但是我做起来还是感觉很麻烦,涉及到一大堆输入、string、排序之类的操作。刚开始做做的很慢,而且因为不熟悉string和char操作,还有各种bug。后来看了一个大神代码才发现竟然可以这么做,几十行代码就完成了,这里就不贴我的垃圾代码了
总之就是:
用:scanf(" %d(%d)",&sco,&psco);
来按照格式读取输入数据(我用的字符串……淦!
然后利用自己写的cmp比较函数进行sort排序(我刚开始用的自己写的……
bool cmp(node a,node b)
{
if(a.ts==b.ts)
{
if(a.fs==b.fs)
return a.n<b.n;
return a.fs<b.fs;
}
return a.ts>b.ts;
}
sort(s,s+j,cmp);
for(int i=0;i<j;i++)
{
printf("%-10s %2d %4d\n",s[i].n,s[i].ts,s[i].fs);
}
扑克排序
太长了……插个原题超链接吧:here。
总的来说,就是分东南西北,指定一个人发牌,发52张牌,按照给定的花色和大小排序输出。
因为牌有TJQKA这种大小的类型,也有CDSH这种花色代号。可以用map之类设定大小,我这里之即采用的数组,用c['K']=13;c['A']=14;
这种方式获得大小。扑克用结构体表示花色和大小;为了保存每个人的牌,我设定了结构体存储人名和扑克结构体。然后就可以利用cmp比较函数和sort直接排序。
最后也是有一些错误没发现导致折腾了很长时间(我以为每个样例都有一个代表结束的#……
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
struct node{
char n;
char x;
};
struct player{
char *name;
node poker[14];
int _count;
};
int c[130]={0};
void precmp()
{
c['C']=1;c['D']=2;c['S']=3;c['H']=4;//花色
c['2']=2;c['3']=3;c['4']=4;c['5']=5;c['6']=6;c['7']=7;c['8']=8;c['9']=9;c['T']=10;c['J']=11;c['Q']=12;c['K']=13;c['A']=14;
}
bool cmp(node a,node b){
if(c[a.n]==c[b.n])
{
return c[a.x]<c[b.x];
}
return c[a.n]<c[b.n];
}
string line1,line2;
player P[5];
int main(){
precmp();
P[0].name="South";P[1].name="West";P[2].name="North";P[3].name="East";
char fp='0';int flag=0;
while(cin>>fp)
{
if(fp=='#')break;
for(int i=0;i<4;i++)P[i]._count=0;
for(int i=0;i<4;i++)if(fp==P[i].name[0]){flag=i+1;flag=flag%4;}//cout<<flag<<"fdvverver"<<endl;}
cin>>line1>>line2;
//char ws='0';cin>>ws;
int sum=0;
for(int i=0;i<26;i++)
{
if(P[flag]._count<14&&flag<4){
P[flag].poker[P[flag]._count].n=line1[sum];sum++;
P[flag].poker[P[flag]._count].x=line1[sum];sum++;
P[flag]._count++;flag=flag+1;flag=flag%4;
}
}
sum=0;
for(int i=0;i<26;i++)
{
if(P[flag]._count<14&&flag<4){
P[flag].poker[P[flag]._count].n=line2[sum];sum++;
P[flag].poker[P[flag]._count].x=line2[sum];sum++;
P[flag]._count++;flag=flag+1;flag=flag%4;
}
}
for(int j=0;j<4;j++)
sort(P[j].poker,P[j].poker+13,cmp);
for(int i=0;i<4;i++)
{
cout<<P[i].name<<" player:"<<endl;
cout<<"+---+---+---+---+---+---+---+---+---+---+---+---+---+"<<endl;
for(int j=0;j<13;j++)
cout<<"|"<<P[i].poker[j].x<<" "<<P[i].poker[j].x;
cout<<"|"<<endl;
for(int j=0;j<13;j++)
cout<<"|"<<" "<<P[i].poker[j].n<<" ";
cout<<"|"<<endl;
for(int j=0;j<13;j++)
cout<<"|"<<P[i].poker[j].x<<" "<<P[i].poker[j].x;
cout<<"|"<<endl;
cout<<"+---+---+---+---+---+---+---+---+---+---+---+---+---+"<<endl;
}
cout<<endl;
}
return 0;
}
经过这周的实验,虽然有在家效率太低了的原因,但是确实主要还是因为自己的菜,这几道题折腾了好多天拖拖拉拉才写完,码力还有待加强