【bfs+优先队列】POJ2049-Finding Nemo
基本上算是普通但略有些繁琐的广搜。给出的墙面和门的坐标为点,而Nemo位于方格中。
【思路】
首先思考一下如何存储下整个坐标系。我们预先约定,用一个方格的左下角顶点坐标来作为这个方格的坐标。map[i][j][k]数组是一个三维数组,下标前两位表示当前方格坐标为(i,j),第三位依次表示方格的上下左右,对应下标中的元素用0表示空白,1表示有墙,2表示有门。读入数据的时候,同时修改该墙或门两侧的方格。注意dx、dy数组一定要与上下左右的方向对应,方便后续操作。最后读入Nemo的坐标只要去尾法强制取整即可。但是看别人讨论,强制取整不能用(int),否则会WA,我试了一下的确会有这种问题。用floor会直接将float赋给int可以解决问题。
广搜时从Nemo开始倒退,由于我们需要比较的是经过最小的门数,而不是走的最小步数,广搜的队列必须要用优先队列存储。
【剪枝】
当没有任何一道门或墙的时候,即m=n=0时,直接输出0;
当Nemo在迷宫最大范围外,即fx>199||fy>199||fx<0||fy<0,直接输出0;
读入数据时记录下门或墙纵坐标的最大最小值,最小值为起点减1,最大值为起点加上长度。当前坐标中某一位大于这个极值时,可以直接走到终点,直接输出此时的步数;
(和上一步略有重复)当前坐标大于门或墙横纵坐标最大值时,只会越走越远,所以可以制止搜索继续。但不能把(nowx<0 || nowy<0 || nowx>limitx || nowy>limity) 写成(nowx<littlex || nowy<littley || nowx>limitx || nowy>limity),因为Nemo倒推时总是越来越趋向于(0,0)点,这么判断会使得大多数情形都变成无解。
1 #include<queue> 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 using namespace std; 6 const int MAXN=205; 7 const int INF=1000000; 8 struct point 9 { 10 int x,y,num; 11 bool operator < (const point &x) const 12 { 13 return num>x.num; 14 /*优先队列设置为小顶堆*/ 15 } 16 }; 17 int m,n; 18 int limitx,limity,littlex,littley,fx,fy; 19 int map[MAXN][MAXN][4]; 20 int step[MAXN][MAXN]; 21 /*方格的坐标用左下角顶点坐标来记录*/ 22 /*第三维依次代表上下左右*/ 23 /*数组内元素的值0:空白 1:墙 2:门*/ 24 int dx[4]={0,0,-1,1}; 25 int dy[4]={1,-1,0,0}; 26 /*d数组要对应上下左右的顺序,方便后续操作*/ 27 int vis[MAXN][MAXN]; 28 float initx,inity; 29 30 void initwall() 31 { 32 memset(map,0,sizeof(map)); 33 limitx=limity=-1; 34 littlex=littley=INF; 35 int x,y,d,t; 36 for (int i=0;i<m;i++) 37 { 38 cin>>x>>y>>d>>t; 39 if (x+t>limitx) limitx=x+t; 40 if (y+t>limity) limity=y+t; 41 if (x-1<littlex) littlex=x-1; 42 if (y-1<littley) littley=y-1; 43 if (d==0) 45 { 46 for (int j=x;j<x+t;j++) 47 { 49 map[j][y][1]=1; 50 if (y>=1) map[j][y-1][0]=1; 51 } 52 } 53 else 54 { 56 for (int j=y;j<y+t;j++) 57 { 58 map[x][j][2]=1; 59 if (x>=1) map[x-1][j][3]=1; 60 } 61 } 62 } 63 } 64 65 void initdoor() 66 { 67 int x,y; 68 int d; 69 for (int i=0;i<n;i++) 70 { 71 scanf("%d%d%d",&x,&y,&d); 72 if (x+1>limitx) limitx=x+1; 73 if (y+1>limity) limity=y+1; 74 if (x-1<littlex) littlex=x-1; 75 if (y-1<littley) littley=y-1; 76 if (d==0) 77 { 78 map[x][y][1]=2; 79 if (y>=1) map[x][y-1][0]=2; 80 } 81 else 82 { 83 map[x][y][2]=2; 84 if (x>=1) map[x-1][y][3]=2; 85 } 86 } 87 } 88 89 int bfs() 90 { 91 for (int i=0;i<=limitx;i++) 92 for (int j=0;j<=limity;j++) step[i][j]=INF; 93 int ans=INF; 94 point in,out; 96 memset(vis,0,sizeof(vis)); 97 step[fx][fy]=0; 98 in.x=fx;in.y=fy;in.num=0; 99 priority_queue<point> que; 100 /*由于队列记录的是经过的门数而非步数,要用优先队列*/ 101 que.push(in); 102 while (!que.empty()) 103 { 104 out=que.top(); 105 que.pop(); 106 if (out.x==0 && out.y==0|| out.x==littlex || out.y==littley || out.x==limitx || out.y==limity) 107 { 108 return out.num; 109 } 110 /*如果到达终点或超出最大范围,直接输出结果*/ 111 for (int i=0;i<4;i++) 112 { 113 int nowx=out.x+dx[i],nowy=out.y+dy[i],nownum=out.num; 114 if (nowx<0 || nowy<0 || nowx>limitx || nowy>limity) continue; 115 if (map[out.x][out.y][i]==1) continue; 116 /*前进的方向不能被门阻隔*/ 117 if (map[out.x][out.y][i]==2) nownum++; 118 /*如果前进的方向是一扇门,则加一*/ 119 if (step[nowx][nowy]==INF || nownum<step[nowx][nowy]&& nownum<ans) 120 { 121 in.x=nowx;in.y=nowy;in.num=nownum; 122 que.push(in); 123 step[nowx][nowy]=in.num; 124 } 125 } 126 } 127 return -1; 128 /*无解时返回-1*/ 129 } 130 131 int main() 132 { 133 while (scanf("%d%d",&m,&n)) 134 { 135 if (m==n && m==-1) break; 136 137 initwall(); 138 initdoor(); 139 cin>>initx>>inity; 140 fx=initx; 141 fy=inity; 142 /*由于方格坐标由左下角定点来表示,强制转换为int型即可*/ 143 if (n==0 && m==0||fx>199||fy>199||fx<0||fy<0) 144 cout<<0<<endl; 145 else cout<<bfs()<<endl; 146 } 147 return 0; 148 }