最小转弯问题【BFS】

Description

给出一张地图,这张地图被分为 n × m ( n , m < = 100 ) n×m(n,m<=100) n×mn,m<=100)个方块,任何一个方块不是平地就是高山。平地可以通过,高山则不能。现在你处在地图的 ( x 1 , y 1 ) (x1,y1) x1,y1这块平地,问:你至少需要拐几个弯才能到达目的地 ( x 2 , y 2 ) (x2,y2) x2,y2?你只能沿着水平和垂直方向的平地上行进,拐弯次数就等于行进方向的改变(从水平到垂直或从垂直到水平)的次数。例如:如图 1 1 1,最少的拐弯次数为 5 5 5


Input
1 1 1行: n ∗ m n*m nm 2 2 2 n + 1 n+1 n+1行:整个地图地形描述(0:空地;1:高山), 如图,第2行地形描述为:1 0 0 0 0 1 0 第3行地形描述为:0 0 1 0 1 0 0 …… 第 n + 2 n+2 n+2行:x1 y1 x2 y2 (分别为起点、终点坐标)

Output
s (即最少的拐弯次数


Sample Input
5 7
1 0 0 0 0 1 0
0 0 1 0 1 0 0
0 0 0 0 1 0 1
0 1 1 0 0 0 0
0 0 0 0 1 1 0
1 3 1 7

Sample Output
5


解题思路

很明显,这又又…又是一道BFS,唯一变化的是让我们统计拐弯。
一共只有4个方向,上下为一种,左右为一种,可以模2判断。
我是把上下设为2,4方向,模2余0,左右设为1,3方向,模2余1,记录方向模2的值。
好了,真棒。。。!(╯▽╰)!
但,你以为你真的做完了吗? 不,因为研究发现(其实是老师讲的),可能有多种方法能到,而因为方向的顺序,你先找到的结果的结果不是最小的结果,所以如果这道题的数据比较水,这种方法你是可以过得,但,还是不要抱着侥幸心理。。。
正解:
最小转弯,不是最少步数。把拓展方式从4个方向一个格改成从4个方向撞到墙为止的一次性拓展完。(用老师的说法就是代价相同)
不过注意:封路和墙不要混为一谈。因为他是撞到墙才停止拓展,而碰到已经被走过的路只不过是不入队而不是不继续拓展。(被卡了好一会qAq)


代码

第一个思路:(不是正解但竟然A了,谁能告诉我为什么,在评论请回复呀)ヾ(◍°∇°◍)ノ゙

#include<iostream>
#include<cstdio>
using namespace std;
const int dx[5]={0,0,1,0,-1};
const int dy[5]={0,1,0,-1,0};
int n,m,a,b,c,d,bsy[200][200];
int st[200][3],fa[200];//st[][1]记录横坐标,st[][2]表示纵坐标,st[][3]记录拐弯数,fa记录方向模2的值
int h=0,t=1;
bool check(int x,int y)
{
	if(x>0&&x<=n&&y>0&&y<=m&&!bsy[x][y])
	return 1;
	else
	return 0;
}
void bfs(){
	fa[1]=-1;//后面有解释
	st[1][1]=a;
	st[1][2]=b;
	st[1][3]=0;
	bsy[a][b]=1;
	do{
		h++;
		for(int k=1;k<=4;k++)
		{
			if(check(st[h][1]+dx[k],st[h][2]+dy[k])){
			   t++;
			   fa[t]=k%2;
			   st[t][1]=st[h][1]+dx[k];
			   st[t][2]=st[h][2]+dy[k];
			   if(fa[t]!=fa[h])
			     st[t][3]=st[h][3]+1;
			     else st[t][3]=st[h][3];
			   bsy[st[t][1]][st[t][2]]=1;//标记走过
			   if(st[t][1]==c&&st[t][2]==d) 
			   {
			      cout<<st[t][3]-1;//-1是因为前面起点的fa设为了-1,第一次走也算成了拐弯
			      return;
			   }
			} 
		}
	}while(h<=t);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
			scanf("%d",&bsy[i][j]);
	}
	scanf("%d%d%d%d",&a,&b,&c,&d);
	bfs(); 
}

正解代码:

#include<cstdio>
using namespace std;
int head,tail,n,m,state[10001][3],c,s,x,y,qx,qy;
bool ok[101][101],a[101][101];
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};//拓展方向
void bfs()
{
	head=0;
	tail=1;
	do
	{
		head++;//出队
		for (int i=0;i<4;i++)//4个方向
		{
			x=state[head][0];y=state[head][1];
			int j=1;
			while (!a[x+dx[i]*j][y+dy[i]*j] && x+dx[i]*j<=n && y+dy[i]*j<=m && x+dx[i]*j>0 && y+dy[i]*j>0)
			//如果碰到墙或边缘就结束拓展
			{
			  if (!ok[x+dx[i]*j][y+dy[i]*j])
			  //如果已经走过的路线就不入队
			  {
			  	tail++;//入队
			  	state[tail][0]=x+dx[i]*j;
			  	state[tail][1]=y+dy[i]*j;//储存位置
			  	state[tail][2]=state[head][2]+1;//储存转弯数
			  	ok[x+dx[i]*j][y+dy[i]*j]=true;//已经走过该地
			  	if (x+dx[i]*j==qx && y+dy[i]*j==qy)//结束判断
			  	{
			  		s=state[tail][2];
			  		return;
			  	}
			  }
			  j++;//下一个拓展
			}
		}
	}
	while(head<tail);//空队退出
}
int main()
{
	scanf("%d%d\n",&n,&m);		
	for (int i=1;i<=n;i++)
	{
	  for (int j=1;j<=m;j++)
	  {
	  	scanf("%d",&c);
	  	if (c!=0) a[i][j]=true;
	  }
	}
	scanf("%d%d%d%d",&state[1][0],&state[1][1],&qx,&qy);
	//输入不解释
	bfs();//函数不解释
	printf("%d",s-1);//输出不解释
}

你可能感兴趣的:(BFS)