道馆之战!!!如图所示,此乃口袋妖怪的道馆战中的龙系道馆,一般情况下,在每一个系列的口袋妖怪中,龙系道馆往往是排列在最后的,我们通过一些滑动可以到达我们所想到达的地方(冰块并不会因此而破碎)。但是,我们仍然需要注意一点,就是我们一旦决定要滑动,就必须沿着这个方向滑动到障碍物的所在之处,才可以停下来,不然就会一直滑动到房间的墙角。
实际上,这样的场景在口袋妖怪中并不仅仅局限于道馆。如图所示,这种场景在口袋妖怪中也是经常出现的:
如图所示,玩家可以决定自己所移动的方向,在冰块的地方,会根据自己所选定的方向进行滑动,在空地或者障碍物的地方,滑动会停下来。玩家可以按照某位大神给出的如下的攻略到达目的地I(中途不要忘记捡起精灵球哦!)
这里,我们探讨一下如何利用DFS算法来设计一个AI,让我们可以得到到达目的地所需要的最小移动的次数。首先,作为一个简化的模型,我们需要对实际问题进行一定程度的修改,我们修改两点:(1)将所有的空地都转换成障碍物,毕竟从Input来看,空地和障碍物的效果其实是等效的(2)我们假设我们可以完全地通过冰面来到达我们所需要的目的地。
那么,原实际问题被简化成为如下的模型:
其中,S代表起始点,G代表终结点。在解决问题之后,我们还需要明确一点,也就是我们所提到的,我们的男主角小智只能持续不断地在冰面上滑动,我的意思是这样的,如图所示:
在(a)中,由于主角的左边和下面是两堵墙,所以,我们不能朝着那个方向移动,但是,我们仍然是可以朝右方和上方移动的。而(b)中则说明,当我们已经移动出去之后(这里只至少移动了一个单位格之后),我们的主角在一段时间内的方向是不可被控制的,只能遵循他原来的方向进行移动。
以上的模型比较简单,这里给出模型的一个最优解:
如图,只要如下四步,我们就可以到达目标点(Goal)了。关于DFS的奥义,前文已经阐述过,所以,这里不解释,直接给出实现(为了增强游戏的可玩性,我们这里只要十步以内的解):
Input:
由于原图是离散的,我们可以用一个二维数组来表述。将原图的问题描述为如下的形式:
6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1
其中的(6,6)代表的是六行六列,0代表冰块,1代表障碍物(或者是空地),2代表起始点,3代表初始点。
Output:对每一组数据来说,利用一个数字来代表你至少要滑行多少次
Solve:
2
3 #include <cstdio>
4
5 // 利用该头文件来处理memset函数
6
7 #include <memory.h>
8
9
10
11 #define MAX 25
12
13 using namespace std;
14
15
16
17 int map[MAX][MAX];
18
19 // 由于是二维数组,所以我们应该记录两个数值,作为一个坐标,对start和end都开一个空间为2个单位的数组
20
21 int start[ 2],end[ 2];
22
23 // 定义上下左右四个方向
24
25 int dir[ 4][ 2]={{ 0,- 1},{ 1, 0},{ 0, 1},{- 1, 0}};
26
27 // 一个地图的长与宽
28
29 int w,h;
30
31 int min_times;
32
33
34
35 void getmap()
36
37 {
38
39 for( int i= 0; i<h; i++)
40
41 {
42
43 for( int j= 0; j<w; j++)
44
45 {
46
47 scanf( " %d ",&map[i][j]);
48
49 if(map[i][j]== 2)
50
51 {
52
53 start[ 0]=i;
54
55 start[ 1]=j;
56
57 }
58
59 }
60
61 }
62
63 }
64
65
66
67 void dfs( int x, int y, int step)
68
69 {
70
71 int sx,sy;
72
73 // 如果十步都无法走出,就直接退出
74
75 if(step> 10) return;
76
77 for( int i= 0; i< 4; i++)
78
79 {
80
81 if(map[x+dir[i][ 0]][y+dir[i][ 1]]!= 1)
82
83 {
84
85 // 找到初始点
86
87 sx=x; sy=y;
88
89 while(map[sx+dir[i][ 0]][sy+dir[i][ 1]]!= 1)
90
91 {
92
93 // 只要没有碰到障碍物,就不断地往前走
94
95 sx=sx+dir[i][ 0];
96
97 sy=sy+dir[i][ 1];
98
99 // 如果越界的话,就回到当前的初始点
100
101 if(sx< 0 || sx>=h || sy< 0 || sy>=w)
102
103 {
104
105 break;
106
107 }
108
109 // 如果到达了终点,修改最短次数,并记录
110
111 if(map[sx][sy]== 3)
112
113 {
114
115 if(min_times>step)
116
117 {
118
119 min_times=step;
120
121 }
122
123 return;
124
125 }
126
127 }
128
129 if(sx>= 0 && sx<h && sy>= 0 && sy<w)
130
131 {
132
133 map[sx+dir[i][ 0]][sy+dir[i][ 1]]= 0;
134
135 dfs(sx,sy,step+ 1);
136
137 // 如果这条路径最终行不通的话,可以将其改成一个障碍物,代表此路不通
138
139 map[sx+dir[i][ 0]][sy+dir[i][ 1]]= 1;
140
141 }
142
143 }
144
145 }
146
147 }
148
149
150
151 int main()
152
153 {
154
155 // 我们这里以(0 0)作为结尾
156
157 while(scanf( " %d%d ",&w,&h)!=EOF && (w+h))
158
159 {
160
161 // 初始化地图
162
163 memset(map, 0, sizeof(map));
164
165 // 欲求最小值,我们必须设置一个最大值,这样才会越来越小
166
167 min_times= 9999;
168
169 // 入读一个地图
170
171 getmap();
172
173 dfs(start[ 0],start[ 1], 1);
174
175 // 这表明不能滑到
176
177 if(min_times== 9999)
178
179 {
180
181 printf( " -1\n ");
182
183 }
184
185 // 输出最小值
186
187 else
188
189 {
190
191 printf( " %d\n ",min_times);
192
193 }
194
195 }
196
197 return 0;
198
199 }
200
201