应该是双向广搜的简单题,虽然写了很久。双向:简而言之就是从起点(正向搜索)和终点(逆向搜索)同时开始搜索,当两个搜索产生的一个子状态相同时就结束搜索。
通常有两种实现方法:
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; }
时间: 2024-12-24 08:18:56