SDU week2作业 (BFS 的两道例题)

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;
}

发布了10 篇原创文章 · 获赞 0 · 访问量 172

猜你喜欢

转载自blog.csdn.net/weixin_45736432/article/details/104673731