吴昊品游戏核心算法 Round 16 —— 吴昊教你玩口袋妖怪 第六弹 龙系道馆

 

  道馆之战!!!如图所示,此乃口袋妖怪的道馆战中的龙系道馆,一般情况下,在每一个系列的口袋妖怪中,龙系道馆往往是排列在最后的,我们通过一些滑动可以到达我们所想到达的地方(冰块并不会因此而破碎)。但是,我们仍然需要注意一点,就是我们一旦决定要滑动,就必须沿着这个方向滑动到障碍物的所在之处,才可以停下来,不然就会一直滑动到房间的墙角。

  实际上,这样的场景在口袋妖怪中并不仅仅局限于道馆。如图所示,这种场景在口袋妖怪中也是经常出现的:

 

  如图所示,玩家可以决定自己所移动的方向,在冰块的地方,会根据自己所选定的方向进行滑动,在空地或者障碍物的地方,滑动会停下来。玩家可以按照某位大神给出的如下的攻略到达目的地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:

 

  1 #include <iostream>
  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> 10return;
 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  

转载于:https://www.cnblogs.com/tuanzang/archive/2013/04/01/2992903.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值