推箱子游戏—广度优先探索版本

只是记录一下自己的学习过程

推箱子游戏—广度优先探索版本

题目描述

推箱子是一款经典游戏。这里我们玩的是一个简单版本,就是在一个N*M的地图上,有1个玩家、1个箱子、1个目的地以及若干障碍,其余是空地。玩家可以往上下左右4个方向移动,但是不能移动出地图或者移动到障碍里去。如果往这个方向移动推到了箱子,箱子也会按这个方向移动一格,当然,箱子也不能被推出地图或推到障碍里。当箱子被推到目的地以后,游戏目标达成。现在告诉你游戏开始是初始的地图布局,请问玩家至少要多少步才能达成目标?

输入
第一行输入两个数字N,M表示地图的大小。其中0<N,M<=12。
接下来有N行,每行包含M个字符表示游戏地图。其中 . 表示空地、X表示玩家、*表示箱子、#表示障碍、@表示目的地。

输出
输出一个数字表示玩家最少需要移动多少步才能将游戏目标达成。当无论如何达成不了的时候,输出-1。

样例输入
6 6
…#…

#*##…
…##.#
…X…
.@#…

样例输出
11

我的简要分析

写这个题目之前写过一个广度优先的跳马问题,跟这个题目差不多,不过障碍的判断不太一样,加上这里有个箱子需要考虑。

如果单纯的用一个二维数组标记人走过的位置,那箱子移动之后人就无法返回,但是实际上推动箱子之后人是可以返回的,所以当箱子移动后,标记人走过位置的数组需要进行重置,相当于箱子的位置跟标记人走过位置的数组是捆绑起来的,所以人走过的位置可以用四维数组进行标记:下标为人目前的位置以及箱子目前的位置。

关于广度优先探索(BFS)推荐这篇博客 链接.

详细代码


    #include <iostream>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    using namespace std;
    
    
    char zmap[20][20];      //地图
    int  jmap[20][20][20][20] = { 0 };         //标记步数以及位置是否走过
    int n1, m1;   //终点位置
    int n2, m2;   //起点位置
    int n3, m3;   //箱子位置
    int n, m;     //地图大小
    int  people[4][2] =     //人走的位置变化
    {
      {-1,0},
      {0,-1},
      {1,0},
      {0,1},
    };            
    
    
    
    //队列
    typedef struct  Sqeue      
    {
    	int* data;
    	int top;
    	int rear;
    }Sqlist;
    
    
    //队列初始化
    void Lcreat(Sqlist* L)     
    {
    	L->data = new int[1000];
    	L->top = 0;
    	L->rear = 0;
    }
    
    
    //入队
    void Lint(Sqlist* L, int e)      
    {
    	L->data[L->rear++] = e;
    }
    
    
    //出队
    int Lout(Sqlist* L)        
    {
    	int e;
    	e = L->data[L->top++];
    	return e;
    }
    
    
    //判断队空
    int Lempty(Sqlist* L)
    {
    	return L->top == L->rear;
    }
    
    
    //判断要走的位置是否符合规定
    int OutMap(int a, int b)    
    {
    	if (a >= 0 && a < n && b >= 0 && b < m && zmap[a][b] != '#')
    		return 0;
    
    	else
    		return 1;
    }
    
    
    //广度优先碳素
    int venture()
    {
    
    	//创建队列并将第一组数据输入,同时标记此位置
    	Sqlist L;
    	Lcreat(&L);
    	int p1, p2, x1, x2;
    	Lint(&L, n2);
    	Lint(&L, m2);
    	Lint(&L, n3);
    	Lint(&L, m3);
    	jmap[n2][m2][n3][m3] = 1;
    
    	//直到队空才停止探索
    	while (!Lempty(&L))
    	{
    		//输出队中第一组数据,若此时箱子位置等于终点位置则返回步数
    		p1 = Lout(&L);
    		p2 = Lout(&L);
    		x1 = Lout(&L);
    		x2 = Lout(&L);
    		if (x1 == n1 && x2 == m1)
    			return jmap[p1][p2][x1][x2] - 1;
    
    		//分别判断此时位置的四个方向是否可走
    		for (int i = 0; i < 4; i++)
    		{
    			//若此方向可走
    			if (!OutMap(p1 + people[i][0], p2 + people[i][1]))
    			{
    				//若此方向到达位置与箱子重合
    				if (p1 + people[i][0] == x1 && p2 + people[i][1] == x2)
    				{
    					//若箱子可以往此方向推进
    					if (!OutMap(x1 + people[i][0], x2 + people[i][1]))
    					{
    						//若此方向(箱子位置改变)到达位置没有走过
    						if (!jmap[p1 + people[i][0]][p2 + people[i][1]][x1 + people[i][0]][x2 + people[i][1]])    //判断要走的位置是否已经走过
    						{
    							//标记走过并且步数在上一个位置的基础上加1
    							jmap[p1 + people[i][0]][p2 + people[i][1]][x1 + people[i][0]][x2 + people[i][1]] = jmap[p1][p2][x1][x2] + 1;
    							//入队这一组数据(人和箱子都往此方向前进)
    							Lint(&L, p1 + people[i][0]);
    							Lint(&L, p2 + people[i][1]);
    							Lint(&L, x1 + people[i][0]);
    							Lint(&L, x2 + people[i][1]);
    						}
    					}
    				}
    
    				//若此方向到达位置没有跟箱子重合
    				else
    				{
    					//若此方向到达位置没有走过
    					if (!jmap[p1 + people[i][0]][p2 + people[i][1]][x1][x2])   
    					{
    						//标记走过并且步数在上一个位置的基础上加1
    						jmap[p1 + people[i][0]][p2 + people[i][1]][x1][x2] = jmap[p1][p2][x1][x2] + 1;
    						//入队这一组数据(只有人往此方向前进)
    						Lint(&L, p1 + people[i][0]);
    						Lint(&L, p2 + people[i][1]);
    						Lint(&L, x1);
    						Lint(&L, x2);
    					}
    				}
    
    			}
    		}
    	}
    
    	//无法到达终点
    	return -1;
    }
    
    
    //主函数
    int main()
    {
    	//输入地图大小
    	cin >> n >> m;  
    
    	//输入元素并标记起点、终点和箱子的位置
    	for (int i = 0; i < n; i++)
    		for (int j = 0; j < m; j++)
    		{
    			//地图元素
    			cin >> zmap[i][j];
    
    			//终点
    			if (zmap[i][j] == '@')
    			{
    				n1 = i; m1 = j;
    			}
    
    			//起点
    			if (zmap[i][j] == 'X')      
    			{
    				n2 = i; m2 = j;
    			}
    
    			//箱子
    			if (zmap[i][j] == '*')
    			{
    				n3 = i; m3 = j;
    			}
    
    		}
    
    
    	//计算并输出所需步数
    	int k;
    	k = venture();
    	cout << k;
    
    }

  • 11
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值