吴昊品游戏核心算法 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 

 

posted on 2013-02-28 15:08  吴昊系列  阅读(670)  评论(0编辑  收藏  举报

导航