很经典的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;
}