POJ 2049 走迷宫选取经过门最少的路线 BFS搜索

很经典的BFS搜索 走迷宫选取经过门最少的路线,这题POJ测试数据设计不全面,changeDir数组赋值错误也可以过。。。

主要图的数据结构存储方式和算法实现参考了http://blog.csdn.net/bobten2008/article/details/5093307

(1)首先是建图, 由于输入给的都是线段, 但是我们平常处理这类问题都是转换为网格来做的, 因此需要将线段转换为网格.转化的方法是对于每个格子,用其左上角点的坐标来表示这个格子。如果其左上角点的坐标是[i][j],那么这个格子就表示为[i][j].将其四周边界的四条线段归这个格子管.即为每个格子建一个数组round[i][j][4],第三维的四个位置分别表示这四条线段的类型(0-3分别对应上下左右): 数组元素的值0表示空气,1表示墙,2表示是一扇门,这样这个模型就建立好了.

(2)其次是bfs的起始点选为Nemo所在的位置,注意要将这个浮点点位置转化为格子的坐标.转化的方法很简单.对于x坐标取整即可,对于y坐标取整+1即可,比如Nemo所在位置为[1.2, 1.3]那么转化为格子的坐标即为:[1, 2].这个坐标即位bfs遍历的起始点
(3)遍历的时候如果所走的方向是墙,则不可走.如果是门则将当前总的steps数+1,如果为空气,steps数不变.另外一点就是如何判重.判重不能简单地记录有没有被访问过,而是需要记录到当前格子的最小步骤.如果当前总步骤数小于当前格子的最小步骤数,则更新其最小步骤数并将这个格子加入队列中.

(4)遍历的终止位置即为题目中的出发位置[0, 0]

Source Code

Problem: 2049 User: yangliuACMer
Memory: 1160K Time: 250MS
Language: C++ Result: Accepted
#include <iostream>
#include <queue>
#define MAX_N 210 
using namespace std;

int v[MAX_N + 1][MAX_N + 1]; //v[i][j]为到达格子[i][j]的最小步骤数
int round[MAX_N + 1][MAX_N][4]; //用左上角点的坐标来指代格子,第三维0、1、2、3指定了该数组元素对应上、下、左、右边,数组的值表明了该边的类型 0空气 1墙 2门
int wn, dn, startXI, startYI, minSteps;//wn墙数量 dn门的数量 起点对应的格子坐标
double startXF, startYF;//起点的输入浮点坐标
int dirarray[4][2] = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};//方向数组,走四个方向时坐标的变换 分别为上、下、左、右

//加入bfs队列的元素类型
struct elem{
	//x、y为这个格子的坐标,dir记录了是从当前格子的那个方向进入到这个格子的, 上0 下1 左2 右3
	int x, y, dir, stepnum;//stepnum为到达当前格子所需要的步骤数量
};
queue<elem> bfsq;

//取当前方向的对面方向
void changeDir(int orginal, int &newDir){
	if(orginal == 0) newDir = 1;
	else if(orginal == 1) newDir = 0;//这里下的反方向肯定是上,POJ测试用例不全面,这里改成1也能AC
	else if(orginal == 2) newDir = 3;
	else newDir = 2;
}

//判断当前坐标是否在合法范围之内
bool inRange(int x, int y){
	return x >= 0 && x <= 205 && y >= 0 && y<= 205;
}

void bfs(){
	//将Nemo的位置加入队列作为bfs搜索的起始位置
	while(!bfsq.empty()) bfsq.pop();
	elem curelem, newelem;
	curelem.x = startXI; curelem.y = startYI; curelem.dir = -1; curelem.stepnum = 0;
	v[startXI][startYI] = 0;
	bfsq.push(curelem);

	int curx, cury, curdir, cursteps, newx, newy, newdir, newsteps, d;
	while(!bfsq.empty()){//实现图的广度遍历即层次式的遍历都要借助于队列
		curelem = bfsq.front();
		bfsq.pop();
		curx = curelem.x; cury = curelem.y; curdir = curelem.dir; cursteps = curelem.stepnum;

		//到达原点
		if(curx == 0 && cury == 0){
			if(cursteps < minSteps)
				minSteps = cursteps;
			continue;
		}
		//遍历当前格子的四个方向,尝试向四个方向走
		for(d = 0; d < 4; d++){
			//不能向回走
			if(d != curdir){
				//所走的方向不能是墙
				if(round[curx][cury][d] != 1){
					//得到新的格子的坐标
					newx = curx + dirarray[d][0];//要注意这种方向数组的用法,很巧妙!
					newy = cury + dirarray[d][1];

					//判定新坐标是否在合法的范围内
					if(inRange(newx, newy)){
						//计算所有相对目标格子所在的方位
						changeDir(d, newdir);

						//如果是门,步骤数+1
						if(round[curx][cury][d] == 2)
							newsteps = cursteps + 1;
						else //如果是空气步骤数目不变
							newsteps = cursteps;

						//判断当前的新格子的新状态是否需要加入队列
						if(v[newx][newy] == 0xbf || newsteps < v[newx][newy] && newsteps < minSteps){
							v[newx][newy] = newsteps;
							newelem.x = newx; newelem.y = newy; newelem.stepnum = newsteps; newelem.dir = newdir;
							bfsq.push(newelem);
						}
					}
				}
			}
		}
	}
}

int main(){
	int i, j, x, y, d, t;
	while(scanf("%d%d", &wn, &dn) && !(wn == -1 && dn == -1)){
		minSteps = INT_MAX;
		memset(v, 12, sizeof(v));
		memset(round, 0, sizeof(round));
		for(i = 1; i <= wn; i++){
			cin>>x>>y>>d>>t;
			//输入预处理,把门和墙转化为格子对应的四面边界
			if(d == 1)//与y轴平行
				for(j = y + 1; j <= y + t; j++)//数组的值为格子类型 0为空气 1为墙 2为门
					round[x][j][2] = round[x - 1][j][3] = 1;//第三维为方向 0上 1下 2左 3右
			else
				for(j = x; j < x + t; j++)
					round[j][y][0] = round[j][y + 1][1] = 1;
		}
		for(i = 1; i <= dn; i++){
			cin>>x>>y>>d;
			if(d == 1)
				round[x][y + 1][2] = round[x - 1][y + 1][3] = 2;
			else 
				round[x][y][0] = round[x][y + 1][1] = 2;

		}
		cin>>startXF>>startYF;//Nemo位置
		startXI = startXF;
		startYI = startYF +1;
		//一场输入处理
		if(startXI < 0 || startXI > 199 || startYI < 0 ||startYI > 199)
			cout<<0<<endl;
		else{
			bfs();
			if(minSteps == INT_MAX) cout<<"-1"<<endl;
			else cout<<minSteps<<endl;
		}
	}
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值