POJ 1915(双向广搜)

来源:互联网 发布:地址栏js执行 编辑:程序博客网 时间:2024/06/12 19:28

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

通常有两种实现方法:

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


1 0
原创粉丝点击