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

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

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

    • 题目描述
    • 我的简要分析
    • 详细代码

题目描述

推箱子是一款经典游戏。这里我们玩的是一个简单版本,就是在一个N*M的地图上,有1个玩家、1个箱子、1个目的地以及若干障碍,其余是空地。玩家可以往上下左右4个方向移动,但是不能移动出地图或者移动到障碍里去。如果往这个方向移动推到了箱子,箱子也会按这个方向移动一格,当然,箱子也不能被推出地图或推到障碍里。当箱子被推到目的地以后,游戏目标达成。现在告诉你游戏开始是初始的地图布局,要求用深度优先搜索找到游戏的解(注意这里不保证步数最少)。
玩家每到一个格子,就按上(U),右®,下(D),左(L)顺时针方向尝试,每一个方向都都在前一个方向失败时才可能尝试。如下图,如果s6为终态,则游戏解为UU; 如果s21为终态,则玩家要尝试UU,UR,UD,UL,RU,RR,RD,RL,…,LD, 才能确定LL是游戏的解。

推箱子游戏—深度优先探索版本_第1张图片
状态由玩家位置和箱子位置构成,算法结构大体如下:
DFS(state s){
for(i = 0; i < 4; i++)
DFS(trans(s, i)); //tans表示状态s往方向i走形成的新状态
}
注意得到解后要立即返回。

输入

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

输出

有解时,输出玩家走的每一步。当无论如何达成不了的时候,输出-1。

样例输入

6 6
…#…

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

样例输出

RRUUULLULDDUURDRRURDDLDDRDLLULLUUDDRRRUUULLULLDRRRLULDDDDRRRUURUULDDDUULLULDDDDRRDRRULLLRRUUULLULDDDD

我的简要分析

这个题目与之前的广度优先版本差不多,不过是使用的探索方法不一样以及需要返回的参数不一样,关于这个问题的分析可以去我的上一篇博客看一下链接。

另外需要注意的是回溯的时候,操作存储是逆序的,所以输出的时候也需要逆序输出。

还有,跟一般的深度探索不一样,若是没有箱子的限制条件,标记位置是否走过的数组在回溯的时候需要清零。但是这个问题,我理解为箱子跟人这样一个位置关系是独立的,所以可以不用清零节约时间,但是起点不能标记,不然回溯之后不好探索其他方向。

强行解释,没有什么道理,懂的大佬可以在评论区解释一下,谢谢。

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

详细代码


#include 
#include 
#include 
#include 
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},
};            
char  road[100007];        //标记人走的路径
int  k = 0;

char  vroad[4] = { 'U','R','D','L' };      //标记行走方向
int  liu = 0;     //记录到达终点以后的返回值


 //判断要走的位置是否符合规定
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(int p1, int p2, int x1, int x2)
{
	//如果箱子位置与终点位置重合则探索结束进行回溯
	if (x1 == n1 && x2 == m1)
	{
		return 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]])   
					{
						//标记走过
						jmap[p1 + people[i][0]][p2 + people[i][1]][x1 + people[i][0]][x2 + people[i][1]] = 1;

                        //如果到达终点得到返回值1则将这一操作存进字符串并回溯
						liu = venture(p1 + people[i][0], p2 + people[i][1], x1 + people[i][0], x2 + people[i][1]);
						if (liu == 1)
						{
							road[k++] = vroad[i];
							liu = 0;
							return 1;
						}
					}
				}
			}

			//若沿此方向走到达位置与箱子位置没有重合
			else
			{
				//若此方向(箱子位置没有改变)到达位置没有走过
				if (!jmap[p1 + people[i][0]][p2 + people[i][1]][x1][x2])     
				{
					//标记走过
					jmap[p1 + people[i][0]][p2 + people[i][1]][x1][x2] = 1;
					liu = venture(p1 + people[i][0], p2 + people[i][1], x1, x2);
					//如果到达终点得到返回值1则将这一操作存进字符串并回溯
					if (liu == 1)
					{
						road[k++] = vroad[i];
						liu = 0;
						return 1;
					}
				}
			}

		}
	}

   //没有到达终点
	return 0;
}



//主函数
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 p1 = n2, p2 = m2, x1 = n3, x2 = m3;
	venture(p1, p2, x1, x2);

	road[k] = '\0';

	//无法到达终点
	if (k == 0)
		cout << "-1";
	//到达终点输出操作
	else
	{
		for (int i = k - 1; i >= 0; i--)
			printf("%c", road[i]);
	}
}

你可能感兴趣的:(学习,推箱子,深度优先探索)