吴昊品游戏核心算法 Round 13 —— 吴昊教你走迷宫(BFS)
如图所示,这是一个手机游戏,走迷宫游戏发展到现在,已经存在不少变式了,比如现在利用android/ios系统的重力感应器功能做成的——《迷宫小 球》就是现在比较成功的一款(关于《迷宫小球》这款经典游戏,我会在《吴昊品工程级别项目源代码》中详细阐述)。我们这里只是抽出游戏的核心算法作为探 讨,其实,这也是我的一个想法,因为UI,美工以及架构等等,在设计的时候不会太难(当然,这里纠正一个误区,就是顶尖的设计师的难度高于顶尖的算法工程 师,但是一般的设计不会追求那种纯粹艺术级别吧!我想)。
BFS的奥义:
BFS是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位址,彻底地搜索整张图,直到找到结果为止。BFS并不使用经验法则算法。
从算法的观点,所有因为展开节点而得到的子节点都会被加进一个先进先出的伫列中。一般的实作里,其邻居节点尚未被检验过的节点会被放置在一个被称为 open 的容器中(例如伫列或是链表),而被检验过的节点则被放置在被称为 closed 的容器中。(open-closed表)
我们的迷宫问题(Source:HDOJ 1728):
Input:整个迷宫地图,可走的位置标为.而不可走的位置标为*(相当于一堵墙)。那么,我们给出最多需要转弯的数目和两个坐标(x1,y1)和(x2,y2)(这里是一个人为限制,所以,我们要用BFS选出一个最佳的策略)。
Output:我们需要判断出这个人能否从一个位置走向另外一个位置,也就是输出yes或者是no就可以了,而具体怎么走很简单。
Solve:
2 Highlights:
3 (1)定义了一个vis[101][101]来计数拐弯数目,并及时将其更改
4 (2)将初始的方向定义为-1,因为初始的时候不存在拐弯不拐弯的问题,可以向任意方向拓展
5 (3)利用STL里面的队列容器来存放结点
6 */
7
8 #include<iostream>
9 //这里开一个队列STL
10 #include<queue>
11 using namespace std;
12
13 //这里开一个二维字符数组来装载地图
14 char map[101][101];
15
16 //n和m为实际地图的长与宽,ei和ej为地图的终点
17 int n,m,T,ei,ej;
18 //方向数组,你懂的
19 int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
20 //特别地,这里标注一个二维数组vis[101][101]来标记每个位置已经拐弯了多少次
21 int vis[101][101];
22
23 //每个结点有一个位置坐标,一个di标注当前的方向,turn标注拐了多少次弯
24 struct node
25 {
26 int x,y,di,turn;
27 };
28
29 //定义一个结点
30 node f;
31
32 void bfs()
33 {
34 //队列结点Q
35 queue<node> Q;
36 //将最开始(first)的那个结点放入队列
37 Q.push(f);
38 //作为初始,我们还没有拐任何一次弯
39 vis[f.x][f.y]=0;
40 while(!Q.empty())
41 {
42 node t=Q.front(),temp;
43 //出队列
44 Q.pop();
45 for(int k=0;k<4;k++)
46 {
47 //从这四个方向中,每次尝试一个不同的方向
48 int i=dir[k][0]+t.x;
49 int j=dir[k][1]+t.y;
50 //出界或者遇到了障碍物,则continue
51 if(i>n||i<1||j>m||j<1||map[i][j]=='*') continue;
52 //如果原来的那个结点对应的方向不是初始方向,而且和原来的方向不一样的话
53 if(t.di!=k&&t.di!=-1)
54 temp.turn=t.turn+1;
55 else temp.turn=t.turn;//方向相同或刚出发
56 //如果超过了次数,则换一种方向
57 if(temp.turn>T) continue;
58 //找到最优解,则输出
59 if(i==ei&&j==ej)
60 {
61 cout<<"yes"<<endl;
62 return;
63 }
64 //及时修正原来的结点,将拐弯数目控制在最小
65 if(vis[i][j]>=temp.turn)
66 {
67 vis[i][j]=temp.turn;
68 temp.x=i;
69 temp.y=j;
70 temp.di=k;
71 Q.push(temp);
72 }
73 }
74 }
75 cout<<"no"<<endl;
76 }
77
78 int main()
79 {
80 int cas;
81 //输入案例的数目
82 cin>>cas;
83 while(cas--)
84 {
85 //地图的长与宽
86 cin>>n>>m;
87 for(int i=1;i<=n;i++)
88 {
89 for(int j=1;j<=m;j++)
90 {
91 cin>>map[i][j];
92 }
93 }
94 //这里的x与y坐标值需要转换
95 cin>>T>>f.y>>f.x>>ej>>ei;
96 //首先标注一个不可能的方向,作为初始方向
97 f.di=-1;
98 //计数拐弯的次数
99 f.turn=0;
100 for(int i=1;i<=n;i++)
101 for(int j=1;j<=m;j++)
102 //记录到这里已经拐弯了多少次
103 vis[i][j]=20;
104 bfs();
105 }
106 return 0;
107 }
108
109