吴昊品游戏核心算法 Round 14 —— 推箱子的一种变式(三个箱子)

 

 

 在更多的情况下,除了很多路障(墙壁)以外,我们会发现眼前的箱子绝对不止一个,只有一个箱子的可玩性绝对是不高的。但是,这里却又有一个显著的问题,就是如果箱子增加的话,问题的(至少是)空间复杂度会呈现指数级别地增加。

 这里面考虑的因素就要复杂许多,但是,总思路还是不变的。我们这里采用分模块的方法来解决,源程序的模块性应该说是蛮好的。

 

  

  1  注明头文件
  2 
  3   #include<iostream>
  4   // queue容器(这一次不用优先队列,而直接用队列了)
  5   #include<queue>
  6   using  namespace std;
  7 
  8   各种数据结构各种调
  9  
 10   // 这里定义一个点(x,y)
 11    struct Point
 12  {
 13     int x,y;       
 14  };
 15  
 16   // 每一个Node代表一个状态
 17    struct Node
 18  {
 19    Point you,box[ 3];
 20     int step;       
 21  };
 22  
 23   int n,m;
 24   char map[ 8][ 8];
 25  Node first,next;
 26  
 27   // 这里乃是八维数组,以及一个方向数组
 28    bool hash[ 8][ 8][ 8][ 8][ 8][ 8][ 8][ 8];
 29   int dir[ 4][ 2]={{ 1, 0},{- 1, 0},{ 0, 1},{ 0,- 1}};
 30 
 31   辅助函数A:设置某个结点的状态
 32  
 33   // 将最开始的状态定义为起始状态,并标记为true
 34    void setHash(Node a)
 35  {
 36    hash[a.you.x][a.you.y][a.box[ 0].x][a.box[ 0].y][a.box[ 1].x][a.box[ 1].y][a.box[ 2].x][a.box[ 2].y]= true;     
 37  }
 38 
 39    辅助函数B:获取某个结点的状态
 40   // 获取某个状态的布尔值
 41    bool getHash(Node a)
 42  {
 43     return hash[a.you.x][a.you.y][a.box[ 0].x][a.box[ 0].y][a.box[ 1].x][a.box[ 1].y][a.box[ 2].x][a.box[ 2].y];     
 44  }  
 45 
 46   辅助函数C:抽取出来,越界和遇到墙都是不可以的
 47 
 48   // 这里拿出来,判断在这个位置是否是合理的,仍然有两种判断标准:(1)是否越界(2)是否有墙壁
 49    bool checkPoint(Point a)
 50  {
 51     if(a.x>= 0&&a.x<n&&a.y>= 0&&a.y<m&&map[a.x][a.y]!= ' # ')
 52    {
 53       return  true;                                                    
 54    }     
 55     return  false;
 56  }
 57  
 58 
 59   辅助函数D:判断这个位置是否有一个箱子
 60   // 这里,判断出是否是一个箱子,而且还可以判断出箱子的编号(也就是这三个箱子的哪一个箱子)
 61    int isBox(Point a)
 62  {
 63     for( int i= 0;i< 3;i++)
 64    {
 65       if(first.box[i].x==a.x&&first.box[i].y==a.y)
 66      {
 67         return i;                                            
 68      }        
 69    }    
 70     return - 1;
 71  }
 72 
 73   辅助函数E:判断这个人是否可以移动这个位置的箱子
 74  
 75   // 判断这个人是否可以推动这个箱子,因为还存在两个以上的箱子叠合的情况,这样的话,就推不动了
 76    bool isCanMove(Point a, int b, int di)
 77  {
 78    Point c;
 79     // 朝着那个人走的方向在前进一步,不过,原来那一步保证是下面有箱子的
 80     c.x=a.x+dir[di][ 0];
 81    c.y=a.y+dir[di][ 1];
 82     // 如果越界的话,当然就不行了
 83      if(!checkPoint(c))
 84    {
 85       return  false;                  
 86    }     
 87     // 如果不越界的话,那么对于三个箱子,扫描一遍
 88      for( int i= 0;i< 3;i++)
 89    {
 90       // 如果是现在的这个箱子,则continue
 91        if(i==b)
 92      {
 93         continue;        
 94      }          
 95       // 这里判断如果那个位置有另外一个箱子的话,则只能返回false了,因为人不可能同时推动两个箱子
 96        else  if(first.box[i].x==c.x&&first.box[i].y==c.y)
 97      {
 98         return  false;     
 99      }
100    }
101     return  true;
102  }
103  
104 
105   辅助函数F:判断游戏是否结束
106   bool isEnd(Node a)
107  {
108     // 这里必须是三个箱子都在地图的叉叉位置上
109      if(map[a.box[ 0].x][a.box[ 0].y]== ' @ '&&map[a.box[ 1].x][a.box[ 1].y]== ' @ '&&map[a.box[ 2].x][a.box[ 2].y]== ' @ ')
110    {
111       return  true;                                                                                                        
112    }     
113     return  false;
114  }
115  
116 
117   初始化函数:将所有的输入读入,并且初始化各种数据结构
118   void init()
119  {
120     // 这个hash数组是八维的,其中的两维给小人,六维给三个箱子
121     memset(hash, 0, sizeof(hash));
122     int i,j;
123     int k= 0;
124     for(i= 0;i<n;i++)
125    {
126       // 这里很容易漏掉,由于读入的是字符型的变量,所以要将回车getchar()掉,不然会混到map的元素中
127       getchar();
128       for(j= 0;j<m;j++)
129      {
130         // 依次读入每个元素
131         scanf( " %c ",&map[i][j]);
132         if(map[i][j]== ' X ')
133        {
134           // 小人的初始位置
135           first.you.x=i;
136          first.you.y=j;                  
137        }   
138         else  if(map[i][j]== ' * ')
139        {
140           // 箱子的初始位置(因为有三个,这里加一个k变量作为下标)
141           first.box[k].x=i;
142          first.box[k++].y=j;     
143        }             
144      }                  
145    }     
146     // 这里设置步数为0,并且加入到Hash八维表中
147     first.step= 0;
148    setHash(first);
149  }
150  
151 
152   广搜开始!
153   // 从这里开始广搜
154    int bfs()
155  {
156     // 定义一个队列结点(还优先队列本质上没多少区别,区别也莫过就是结构上的)
157     queue<Node>Q;
158    Q.push(first);
159     // 开始完全搜索
160      while(!Q.empty())
161    {
162      first=Q.front();
163      Q.pop();
164       // 判断整个游戏是否结束了
165        if(isEnd(first))
166      {
167         return first.step;                
168      }                 
169       // 朝着四个方向分别搜索
170        for( int i= 0;i< 4;i++)
171      {
172        next=first;
173        next.you.x=first.you.x+dir[i][ 0];
174        next.you.y=first.you.y+dir[i][ 1];
175         // 这里提前走一步,因为有三个箱子,所以提前注明了
176         next.step=first.step+ 1;
177         // 这里仍然是判断两个情况,一种是碰到箱子了,一种是没有碰到箱子
178          if(checkPoint(next.you))
179        {
180           // 由专门的函数来判断是否是箱子
181            int k=isBox(next.you);
182           if(k!=- 1)
183          {
184             // 如果可以移动箱子的话,将箱子移动
185              if(isCanMove(next.you,k,i))
186            {
187              next.box[k].x+=dir[i][ 0];
188              next.box[k].y+=dir[i][ 1];
189               // 如果还没有访问的话,这里标记,并进入队列
190                if(!getHash(next))
191              {
192                setHash(next);
193                Q.push(next);                 
194              }                           
195            }         
196          }
197           else
198          {
199             // 标记,但是箱子不进行移动
200              if(!getHash(next))
201            {
202              setHash(next);
203              Q.push(next);                  
204            }    
205          }                        
206        }        
207      }
208    }    
209     // 如果无解的话,这里返回-1(对于是否有解,也只能由计算机来判断了)
210      return - 1;
211  }
212  
213 
214   主函数很精简,要的其实就是这个效果,辅助功能都有辅助函数来完成
215   int main()
216  {
217     while(scanf( " %d%d ",&n,&m)!=EOF)
218    {
219       // 主函数很简洁,先初始化整个地图,然后就考虑输出了,输出的时候要调用bfs()函数
220       init();
221      printf( " %d\n ",bfs());                               
222    }
223     return  0;    
224  }
225 
226 

 

你可能感兴趣的:(round)