Maze:
题目:
东东有一张地图,想通过地图找到妹纸。地图显示,0表示可以走,1表示不可以走,左上角是入口,右下角是妹纸,这两个位置保证为0。既然已经知道了地图,那么东东找到妹纸就不难了,请你编一个程序,写出东东找到妹纸的最短路线。
Input:
输入是一个5 × 5的二维数组,仅由0、1两数字组成,表示法阵地图。
Output:
输出若干行,表示从左上角到右下角的最短路径依次经过的坐标,格式如样例所示。数据保证有唯一解。
**思路:**主要问题就是如何从起点找到到终点的路径,使用的是BFS,每次都是由当前节点向外拓展一层,符合条件的加入,直到遇到终点为止。
总结:
这算是一个比较典型的BFS问题,因此在这里由这道题总结一下用BFS 做题的一般性情况。BFS常用的数据结构是队列,首先就是将起点放入队列,然后判定条件 !(q.empty),然后讲队列首元素取出来判定终点,紧接着就是以当前元素为起点,向外拓展一层,将新的元素加入队列,也可以称为是状态转移。至于具体的就需要判断新的元素是否符合加入的条件,在本题中就是没有超出边界范围,并且之前没有访问过。
以上是总体上的内容,接下来就将一下这个题细节性问题。
(1)为方便上下左右四个状态,可以在原本的范围上拓宽一周,然后便于边界点的判断,由55的方阵变成了77的方阵,顺便为便于标记到达情况,可将四周的墙标记为-1 ,表示不可达,未经过点是0 ,已到达点是1。
(2)如何记录路径的问题:我在这个实验中是找到了看两种方法。第一种,用二维数组表示前向,在新拓展一层点的时候,记录这个点的前向点:pre[x][y]=now;
然后利用递归的方法输出:
void output(point now)
{
if(!(now.x==1&&now.y==1)) //起始位置
{
output(pre[now.x][now.y]);
}
printf("(%d, %d)\n",now.x-1,now.y-1);
}
假设题目是不计算具体的路径而只要求输出需要多少步的时候,可以利用这样数组的思想,记录距离:
dis[x][y]=dis[now.x][now.y]+1;
直接输出最后的距离就是总距离。
第二种方法是仍然用数组记录前向: pre[xx][yy]=point(x,y); //记录前向
然后由于这样是只能从后往前记录,但是需要从前往后输出,此时就利用栈输出,已知最终的点坐标,依次压入栈,最后从栈中弹出输出。
stack<point> S;
int xx=4,yy=4;
while(true)
{
S.push(point(xx,yy));
if(xx==0&&yy==0) break;
int x=xx,y=yy;
xx=pre[x][y].x;
yy=pre[x][y].y;
}
while(!S.empty())
{
point p=S.top(); S.pop();
printf("(%d, %d)\n",p.x,p.y);
}
代码:
//地图加上墙这一边界
//定义每次走的四个方向
//记录前向,这个点的上一个点是什么
#include<stdio.h>
#include<iostream>
#include<queue>
#include<string.h>
using namespace std;
const int maxn=7;
struct point
{
int x;
int y;
/*point(int a,int b)
{
x=a;y=b;
}*/
};
point pre[7][7];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int maze[maxn][maxn]; // 0 1 2 3 4 5 6
bool vis[maxn][maxn]={false};
int dis[maxn][maxn]={0};
void output(point now)
{
if(!(now.x==1&&now.y==1)) //起始位置
{
output(pre[now.x][now.y]);
}
printf("(%d, %d)\n",now.x-1,now.y-1);
}
void bfs()
{
point s;
s.x=1;s.y=1;
dis[s.x][s.y]=0;
queue<point> q;
q.push(s);
while(!q.empty())
{
point now=q.front(); q.pop();
if(now.x==5&&now.y==5)
{
output(now);
break ;
}
for(int i=0;i<4;i++)
{
int x=now.x+dx[i];
int y=now.y+dy[i];
if(x>=1&&x<=6&&y>=1&&y<=6&&(maze[x][y]==0)&&(!vis[x][y]))
{
point next;
next.x=x;
next.y=y;
q.push(next);
vis[x][y]=true;
pre[x][y]=now;
dis[x][y]=dis[now.x][now.y]+1;
}
}
}
}
int main()
{
memset(maze,0,sizeof(maze));
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
scanf("%d",&maze[i][j]);
}
}
bfs();
//printf("%d",dis[5][5]);
return 0;
}
Pour water:
**题目:**倒水问题 “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列开始,不应该有空行或任何尾随空格。
思路: 这个题目 我感觉隐含了一些信息,比如水的来源是无限的,可以一直从饮水机中倒水,如果要将某个杯子倒空的话,不必关心水是倒在哪里。然后这个题就是2个杯子,针对其中水的不同情况,有六种转移状态。整个过程的起点是AB杯皆空,终点是A 杯或者B杯的水为C,在每一次状态下,都有可能存在6种能转移的状态(当然中间有不合适的,不合适的舍弃即可)。
同时因为要求输出每个过程的操作,因此需要记录下这一步使怎么得来的,因为只有6种情况,因此可以考虑在结构体种加入状态,根据不同的操作,在验证是否加入的时候更改状态。
输出也是仿照上例,通过前向数组,递归输出。
总结:
因为这两个题都分别属于BFS 的显式和隐式题,在这里总结一下,什么样的题可以考虑用BFS 求解,分析有以下特征
(1)有起点和终点
(2)起点由有限次的且每次有限个的状态转移可以到达终点
(3)求最短路径的时候
代码:
#include<iostream>
#include<queue>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=1001;
struct node
{
int A;
int B;
int status; //用6 个数字表示状态
bool operator<(const node &s) const
{
return A!=s.A ? A<s.A : B<s.B ;
}
bool operator==(const node &s) const
{
return A == s.A && B == s.B;
}
};
bool vis[maxn][maxn];
node pre[maxn][maxn];
void trans(node t)
{
switch(t.status)
{
case 1:
cout<<"fill B"<<endl;
break;
case 2:
cout<<"empty B"<<endl;
break;
case 3:
cout<<"fill A"<<endl;
break;
case 4:
cout<<"empty A"<<endl;
break;
case 5:
cout<<"pour A B"<<endl;
break;
case 6:
cout<<"pour B A"<<endl;
break;
default:
break;
}
}
void output(node t)
{
if(t.A!=0||t.B!=0) //起始位置
{
output(pre[t.A][t.B]);
trans(t);
}
}
void bfs(int a,int b,int c)
{
queue<node> q;//队列
node s,t;
memset(vis,false,sizeof(vis)); //将vis 中的值全部置为false 未到达过
memset(pre,0,sizeof(pre)); //同上
s.A=s.B=0;//起点两杯水均为空
vis[s.A][s.B]=true;
q.push(s);
while(!q.empty())
{
s=q.front();
q.pop();
if(s.A==c||s.B==c)//完成
{
output(s);
cout<<"success"<<endl;
return ;
}
if(s.B<b) //b中水不满,倒满B杯中的水 fill B
{
t.B=b;
t.A=s.A;
if(!vis[t.A][t.B]) //未曾到达这种状态
{
t.status=1;
q.push(t);
vis[t.A][t.B]=true;
pre[t.A][t.B]=s;
}
}
if(s.B>0) //b中有水,倒空B杯中的水
{
t.B=0;
t.A=s.A;
if(!vis[t.A][t.B])
{
t.status=2;
q.push(t);
vis[t.A][t.B]=true;
pre[t.A][t.B]=s;
}
}
if(s.A<a)//a中水不满,倒满A杯中的水
{
t.A=a;
t.B=s.B;
if(!vis[t.A][t.B])
{
t.status=3;
q.push(t);
vis[t.A][t.B]=true;
pre[t.A][t.B]=s;
}
}
if(s.A>0)//a中有水,倒空a杯中的水
{
t.A=0;
t.B=s.B;
if(!vis[t.A][t.B])
{
t.status=4;
q.push(t);
vis[t.A][t.B]=true;
pre[t.A][t.B]=s;
}
}
if(s.A!=0&&s.B<b) //a中有水且b中水不满,考虑a杯倒入b杯
{
if(s.A+s.B<=b)
{
t.B=s.A+s.B;
t.A=0;
if(!vis[t.A][t.B])
{
t.status=5;
q.push(t);
vis[t.A][t.B]=true;
pre[t.A][t.B]=s;
}
}
else
{
t.B=b;
t.A=s.B+s.A-b;
if(!vis[t.A][t.B])
{
t.status=5;
q.push(t);
vis[t.A][t.B]=true;
pre[t.A][t.B]=s;
}
}
}
if(s.B!=0&&s.A<a)//a中有水且b中水不满,考虑b杯倒入a杯
{
if(s.A+s.B<=a)
{
t.A=s.A+s.B;
t.B=0;
if(!vis[t.A][t.B])
{
t.status=6;
q.push(t);
vis[t.A][t.B]=true;
pre[t.A][t.B]=s;
}
}
else
{
t.A=a;
t.B=s.B+s.A-a;
if(!vis[t.A][t.B])
{
t.status=6;
q.push(t);
vis[t.A][t.B]=true;
pre[t.A][t.B]=s;
}
}
}
}
}
int main()
{
int a,b,c;
while(scanf("%d %d %d",&a,&b,&c)!=EOF)
{
bfs(a,b,c);
}
return 0;
}