POJ 1915(双向广搜)

应该是双向广搜的简单题,虽然写了很久。双向:简而言之就是从起点(正向搜索)和终点(逆向搜索)同时开始搜索,当两个搜索产生的一个子状态相同时就结束搜索。

通常有两种实现方法:

1、用一个队列来储存子状态,起点和终点先后入队,正向搜索和逆向搜索交替进行,两个方向的搜索交替扩展子状态。直到两个方向的搜索产生相同的子状态结束。

2、两个方向的搜索虽然是交替扩展子状态的。但是两个方向生成的子状态的速度不一定平衡。所以,可以每次选择子状态数较少的那个方向先进行扩展。这样就不会出现两个方向生成子状态的速度的不平衡,可以明显的提高效率哦。


这里只给出用第一种方法实现的代码。因为双向广搜要判断两个方向搜索产生的子状态是否相同,所以要用到标记数组和记录结果的数组一个或两个任选,这里用的是一个数组来标记,正向标记为1,逆向标记为2,0表示未访问。

/**
**author :Skylon **
╭︿︿︿╮
{/ A  C /} 
 ( (OO) ) 
  ︶︶︶ 
**    **
**POJ_1915题**
** 2014 年 7月 19日**
**/
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cctype>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define maxM 310
#define maxN 1000000
using namespace std;
int n,sx,sy,ex,ey;//图的大小,起点,终点
int dir[16]={1,2,-1,2,1,-2,-1,-2,2,1,2,-1,-2,1,-2,-1};//搜索的方向,用一维素组来存储的,每相邻的两个数表示一个搜索方向,16/2个方向。注意循环时的下标处理。
int xx[maxN],yy[maxN];//数组模拟队列
int vis[maxM][maxM],step[maxM][maxM];//vis为标记,step为结果
int fun()
{
	memset(step,0,sizeof(step));
	memset(vis,0,sizeof(vis));
	int head=0,tail=0;//初始化头尾指针
	xx[tail]=sx,yy[tail++]=sy,vis[sx][sy]=1;//起点入队,并标记为1,正向搜索
	xx[tail]=ex,yy[tail++]=ey,vis[ex][ey]=2;//终点入队,并标记为2,逆向搜索
	while (head!=tail)
	{
		int x=xx[head],y=yy[head++],t=step[x][y];//x、y取队首。head++,出队。t记录此时的步数
		for (int i=0;i<16;)//16/2个方向搜索
		{
			int l=x+dir[i++],r=y+dir[i++];
			if (r<0||r>=n||l<0||l>=n)//边界判断
				continue ;
			if (vis[x][y]!=vis[l][r]&&vis[x][y]&&vis[l][r])//当正向搜索和逆向搜索产生的子状态相同时,结束搜索
				return step[l][r]+step[x][y]+1;//返回正向搜索的步数+逆向搜索的步数+搜索到该相同子状态的步数
			if (!vis[l][r])//如果没有达到该状态,则继续入队。不能用if(vis[l][r]==0)因为vis标记的是正向和逆向两个状态
			{
				xx[tail]=l,yy[tail++]=r;//入队
				step[l][r]=t+1;//记录步数
				vis[l][r]=vis[x][y];//标记,将前一个状态的值赋给当前状态,而不是用1、2赋值,就能保证正向搜索产生子状态被标记为正向;逆向搜索产生的子状态被标记为逆向
			}
		}
	}
	return 0;//起点和终点相同
}
int main()
{
	int t;
	scanf("%d",&t);
	while (t--&&scanf("%d%d%d%d%d",&n,&sx,&sy,&ex,&ey))
		printf("%d\n",fun());
	return 0;
}


你可能感兴趣的:(搜索,poj,广搜,双向广搜)