机试算法讲解: 第41题 广度优先搜索之迷宫问题

/*
知识:我被魔王抓走,城堡是A*B*C的立方体,即A个B*C的矩阵,我被关在(0,0,0)位置,出口在(A-1,B-1,C-1),魔王在T分钟内回到城堡,我每分钟能移动1个坐标。若
     走到出口恰遇魔王,也算成功。请输出多少分钟可以离开,不能则输出-1
问题:
输入:第一行是1个正整数K,表名测试数据的数量。每组测试数据的第一行时4个正整数A,B,C和T(1<=A,B,C<=50,1<=T<=1000,)分别代表城堡的大小和魔王回来的时间
     然后是A块输入数据(先是0块,1块,2块。。),每块输入数据有B行,每行有C个正整数,代表迷宫的布局,其中0代表路,1代表墙
输出:如果能离开,输出多少分钟,否则输出-1
输入:
1
3(3个) 3(行号) 4(列号) 20(魔王时间20秒)
0 1 1 1(0表示路,1表示墙)
0 0 1 1
0 1 1 1

1 1 1 1
1 0 0 1
0 1 1 1

0 0 0 0 
0 1 1 0
0 1 1 0
输出:
11

思路:
1 用mark[50][50][50]做标记数组,初始为false,已经搜索过则置为true,已经搜索过不再重复搜索
2 用设置坐标变换数组,进行状态的迁移
3 由于采用将(0,0,0)认为初始根节点,因为如果前面搜索到某状态,后面再次搜索到该状态必定所耗时间>=上一次搜索时间,因此后面索索过得一律不再搜索
4 用maze[][][]标识0路,1墙
5 广度优先遍历,必须用队列
6 设置初始节点时,不要忘记将其访问标记置为true
7 注意判断是否在城堡中,x <= a-1 而不是 x < a
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <queue>

using namespace std;

#define N 50

typedef struct Stat
{
	int x,y,z;//坐标
	int t;//从(0,0,0)到达该坐标的时间
}Stat;//保存当前节点的状态

int maze[N][N][N];//用于标识每一个坐标是0:路,1:墙
bool mark[N][N][N];//用于标识该坐标是否已经搜索过,false:未搜索过,true:搜索过,便于剪枝
int goNext[][3] = 
{1,0,0,
-1,0,0,
0,1,0,
0,-1,0,
0,0,1,
0,0,-1};//用于进行下一次6个可达状态的遍历

queue<Stat> queueStat;

//进行深度遍历,无需判断超时
int BFS(int a,int b,int c)
{
	//只要队列不空,说明仍有状态需要遍历
	while(!queueStat.empty())
	{
		//弹出当前状态,进行状态迁移
		Stat stat = queueStat.front();
		queueStat.pop();

		//遍历6种状态
		int x,y,z;
		for(int i = 0 ; i < 6 ; i++)
		{
			x = stat.x + goNext[i][0];
			y = stat.y + goNext[i][1];
			z = stat.z + goNext[i][2];

			//判断是否仍在围墙中
			//易错,这里城堡的位置是不能为a,因为这是数组,这样做就超过了
			//if(x < 0 || x > a || y < 0 || y > b || z < 0 || z > c)
			if(x <0 || x >= a || y < 0 || y >=b || z < 0 || z >= c )
			{
				continue;
			}

			//如果已经遍历过,则跳过
			if(true==mark[x][y][z])
			{
				continue;
			}
			//如果下一个是墙,则跳过
			if(1==maze[x][y][z])
			{
				continue;
			}

			//更新状态
			Stat statTemp;
			statTemp.x = x;
			statTemp.y = y;
			statTemp.z = z;
			statTemp.t = stat.t + 1;//所耗时间进行累加

			//易错,更新剪枝状态
			mark[x][y][z] = true;

			//将新状态放入队列
			queueStat.push(statTemp);

			//判断是否已经到达终点,则返回其所耗时间
			if(a-1==x && b-1==y && c-1==z)
			{
				return statTemp.t;
			}
		}//for
	}//while
	return -1;//如果一直没有找到,返回-1
}

int main(int argc,char* argv[])
{
	int k;
	while(EOF!=scanf("%d",&k))
	{
		//开始将队列清空
		while(!queueStat.empty())
		{
			queueStat.pop();
		}
		while(k--)
		{
			int a,b,c,t;
			scanf("%d %d %d %d",&a,&b,&c,&t);
			for(int i = 0 ; i < a ; i++)
			{
				for(int j = 0 ; j < b ; j++)
				{
					for(int m = 0 ; m < c ; m++)
					{
						//输入迷宫中的当前点的状态:路还是墙,迷宫点的坐标有maze[i][j][m]下标标识
						scanf("%d",&maze[i][j][m]);
						//易错,将状态标记
						mark[i][j][m] = false;
					}
				}
			}
			//初始化起始节点信息
			Stat stat;
			stat.x = 0;
			stat.y = 0;
			stat.z = 0;
			stat.t = 0;
			//易错,还要设置初始节点为已经遍历
			mark[0][0][0] = true;

			//将起始节点信息放入状态队列
			queueStat.push(stat);
			int iRes = BFS(a,b,c);
			//易错,需要将花费时间做判断
			if(iRes <= t)
			{
				printf("%d\n",iRes);
			}
			else
			{
				printf("-1\n");
			}
		}
	}
	system("pause");
	getchar();
	return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值