1147.木乃伊迷宫
时限:1000ms 内存限制:10000K 总时限:3000ms
描述
木乃伊地下宫殿是一个6行6列的迷宫。游戏在木乃伊所在的迷宫里展开,任务就是尽快赶到出口。你一次只能走一步,而木乃伊可以走两步,但木乃伊是很笨的,他总是先尽量跟你达到同一列,如果已经是同一列了,他才会向你走来,有墙的地方人和木乃伊都不能过,你可以利用障碍物牵制住木乃伊。
输入
先输入墙的数量n,然后在后续的n行里每行有3个数表示一堵墙,3个数分别为格子的行、列和墙的位置(0表示这个格子的下方是墙,1表示这个格子的右方是墙),再下来的3行每行2个数,分别表示木乃伊、人还有出口的位置。
输出
如果能安全逃生则输出Yes,否则输出No,答案占一行。
新鲜出炉的第二版代码(更简洁清晰明了):
#include <iostream>
#include <queue>
using namespace std;
int n;
int maze[6][6][2]; //存储迷宫的墙
struct node
{
int mx,my; //木乃伊位置
int px,py; //人位置
bool useful; //这个节点是否有效
}; //无效条件:越界、重复、被抓
node start,target;
queue <node> q1;
int used[6][6][6][6]; //判重
int walk[4][2]= //返回人走一格的新位置坐标
{
0, -1, //左
+1, 0, //下
0, +1, //右
-1, 0 //上
};
void input(); //输入函数
bool bfs();
node moveto(node cur, int i); //返回节点cur扩展的下一个节点next
bool isWall(int x, int y, int i); //判断方格[x,y]方向是否是墙
int main()
{
input();
if(bfs())
{
cout<<"Yes"<<endl;
}
else
{
cout<<"No"<<endl;
}
return 0;
}
void input()
{
//第一行
cin>>n;
//第二行
int i,j,x;
for(int k=0; k<n; k++)
{
cin>>i>>j>>x;
maze[i][j][x]=1; //1代表是墙
}
//最后三行
cin>>start.mx>>start.my;
cin>>start.px>>start.py;
cin>>target.px>>target.py;
//初始节点标记并入队
used[start.mx][start.my][start.px][start.px]=1;
q1.push(start);
}
bool bfs()
{
node cur,next;
while(!q1.empty())
{
cur=q1.front();
q1.pop();
for(int i=0; i<4; i++) //人要向四个方向试探
{
next=moveto(cur, i); //获取扩展得到的下一个节点
if(next.useful==true) //若该节点有效
{
if(next.px==target.px&&next.py==target.py) //若到达目标则返回true
{
return true;
}
else //未到达目标则入队
{
q1.push(next);
}
}
}
}
return false; //所有情况都不能到达目标,返回false
}
//该函数返回节点cur扩展的新节点next
//人要向i方向走一步,木乃伊要向人靠近两步
//节点无效条件:人越界、人撞墙、人被木乃伊抓到、节点next重复
node moveto(node cur, int i)
{
node next;
//人向i方向走一步
next.px=cur.px+walk[i][0]; //获取新坐标
next.py=cur.py+walk[i][1];
if((next.px<0||next.px>=6||next.py<0||next.py>=6) //若人越界
||
(isWall(cur.px, cur.py, i))) //若撞墙
{
next.useful=false; //该节点无效,直接返回
return next;
}
//木乃伊走两步
next.mx=cur.mx;
next.my=cur.my;
int step=2;
while(step--)
{
//若列数不相等,则先到达同一列
if(next.py>next.my&&!isWall(next.mx, next.my, 2))
{
next.my++;
}
else if(next.py<next.my&&!isWall(next.mx, next.my, 0))
{
next.my--;
}
//若列数相等,则再到达同一行
else if(next.py==next.my)
{
if(next.px>next.mx&&!isWall(next.mx, next.my, 1))
{
next.mx++;
}
else if(next.px<next.mx&&!isWall(next.mx, next.my, 3))
{
next.mx--;
}
//若同行同列,则人被抓住,此节点无效,直接返回
else if(next.px==next.mx)
{
//cout<<next.px<<' '<<next.py<<"catch"<<endl;
next.useful=false;
return next;
}
}
}
//人和木乃伊都走完了
if(used[next.mx][next.my][next.px][next.py]) //若节点重复,则节点无效
{
//cout<<next.px<<' '<<next.py<<"used"<<endl;
next.useful=false;
}
else
{
used[next.mx][next.my][next.px][next.py]=1; //否则标记该节点到达过
next.useful=true; //标记该节点有效
}
return next;
}
//判断方格[x,y]方向是否是墙
bool isWall(int x, int y, int i)
{
switch(i)
{
case 0: //向左走
return maze[x][y-1][1]; //判断左边的方格的右侧是否是墙
case 1: //向下走
return maze[x][y][0]; //判断本格的下侧是否是墙
case 2: //向右走
return maze[x][y][1]; //判断本格的右侧是否是墙
case 3: //向上走
return maze[x-1][y][0]; //判断上边的方格的下侧是否是墙
}
return -1; //返回-1代表i值错误
}
【10月27日后记】
1.重写了这道题,然而写+调试也用了五十分钟orz,按照四个小时八道题的考试时间来算emmmm我这怕是凉了啊
2.第二次写的时候出现的问题是:以前都是判断不越界,条件之间用&&习惯了,然而这道题的代码里是判断越界,所以条件之间应该用||
3.自己感觉第二版比第一版好看多了,第一版写的什么玩意儿
10月12日的第一版代码(又臭又长冗余难看但不舍得删):
#include <iostream>
#include <queue>
using namespace std;
int maze[6][6][2]; //存储迷宫的墙
int used[6][6][6][6]; //判重数组
struct node //状态节点
{
int px,py; //people-人的横纵坐标
int mx,my; //mummy-木乃伊的横纵坐标
};
int tx,ty; //target-目标位置横纵坐标
queue <node> q1; //广搜所用队列
bool bfs(); //广搜
bool canpeoplemove(node n1, int d); //人能否向方向d走一步
node peoplemove(node n1, int d); //令人向方向d走一步,返回新状态
node mummymove(node n1); //令木乃伊按照规则走两步,返回新状态
bool isNew(node n1); //判重函数
bool isSafe(node n1); //判断木乃伊和人是否在一个格子
bool isIntarget(node n1); //判断人是否到达目标位置
int main()
{
int n;
cin>>n; //输入墙数
int a,b,c;
for(int i=0; i<n; i++) //输入迷宫的墙
{
cin>>a>>b>>c;
maze[a][b][c]=1; //为1代表有墙,c=0表示格子下方,c=1表示格子右方
}
node start; //输入初始位置的状态节点
cin>>start.mx>>start.my>>start.px>>start.py;
q1.push(start); //节点入队
used[start.mx][start.my][start.px][start.py]=1; //标记状态用过
cin>>tx>>ty; //输入目标位置
if(isIntarget(start)) //如果一开始人就站在目标位置
{
cout<<"Yes"<<endl;
}
else if(!isSafe(start)) //如果一开始木乃伊就和人在一个格子里
{
cout<<"No"<<endl;
}
else //排除坑,开始广搜
{
if(bfs())
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}
bool bfs() //广搜
{
node top,next;
while(!q1.empty())
{
top=q1.front();
q1.pop();
for(int i=0; i<4; i++) //向4个方向搜索,0==左,1==下,2==右,3==上
{
if(canpeoplemove(top, i)) //判断人能否向方向i走一格
{
next=peoplemove(top, i); //人向方向i走一格,返回新状态next
next=mummymove(next); //木乃伊根据规则走两格,返回新状态next
if(isNew(next)) //判重
{
if(isSafe(next)) //判断人是否安全
{
if(isIntarget(next)) //判断人是否到达目标位置
{
return true;
}
else
{
q1.push(next); //新状态入队
used[next.mx][next.my][next.px][next.py]=1; //记录该状态用过
}
}
else //如果此时人不安全,则不扩展该节点,继续搜索
{
continue;
}
}
}
}
}
return false; //队列为空,证明搜索所有节点后都没有返回true,无法逃出
}
//判断人能否向方向d走一格
//不能走的条件:越界,有墙
bool canpeoplemove(node n1, int d)
{
node n2;
n2.mx=n1.mx;
n2.my=n1.my;
n2.px=n1.px;
n2.py=n1.py;
switch(d)
{
case 0: //人要向左走一格
{
if(n2.py==0 //越界警告!
||maze[n2.px][n2.py-1][1]==1) //【左边那个格子】的右边是墙
return false;
else
{
break;
}
}
case 1: //下
{
if(n2.px==5||maze[n2.px][n2.py][0]==1) //越界或者【本格】的下边是墙
return false;
else
{
break;
}
}
case 2: //右
{
if(n2.py==5||maze[n2.px][n2.py][1]==1) //道理同上
return false;
else
{
break;
}
}
case 3: //上
{
if(n2.px==0||maze[n2.px-1][n2.py][0]==1)
return false;
else
{
break;
}
}
}
return true;
}
//人要向方向d走一格,返回新状态节点n2
node peoplemove(node n1, int d)
{
node n2;
n2.mx=n1.mx;
n2.my=n1.my;
n2.px=n1.px;
n2.py=n1.py;
switch(d)
{
case 0: //左
{
n2.py--;
break;
}
case 1: //下
{
n2.px++;
break;
}
case 2: //右
{
n2.py++;
break;
}
case 3: //上
{
n2.px--;
break;
}
}
return n2;
}
//木乃伊要按照规则走两格,返回新状态节点n1
node mummymove(node n1)
{
int n=2; //走两格
while(n)
{
n--;
if(n1.my<n1.py) //木乃伊所在列<人所在列
{
if(maze[n1.mx][n1.my][1]!=1) //没有墙才可以向右走,有墙就被挡住,此步作废
{
n1.my++;
}
}
else if(n1.my>n1.py) //木乃伊所在列>人所在列
{
if(maze[n1.mx][n1.my-1][1]!=1) //没有墙才可以向左走,有墙就被挡住,此步作废
{
n1.my--;
}
}
else //木乃伊和人在同一列
{
if(n1.mx>n1.px) //木乃伊所在行>人所在行
{
if(maze[n1.mx-1][n1.my][0]!=1) //没有墙才可以向上走,有墙就被挡住,此步作废
{
n1.mx--;
}
}
else if(n1.mx<n1.px) //木乃伊所在行<人所在行
{
if(maze[n1.mx][n1.my][0]!=1) //没有墙才可以向下走,有墙就被挡住,此步作废
{
n1.mx++;
}
} //如果木乃伊和人在同行同列,那么木乃伊一格也不走
}
}
return n1;
}
//判重函数
bool isNew(node n1)
{
if(used[n1.mx][n1.my][n1.px][n1.py]==1)
{
return false;
}
else
{
return true;
}
}
//判断人是否安全
bool isSafe(node n1)
{
if(n1.mx==n1.px&&n1.my==n1.py) //人和木乃伊同行同列
{
return false;
}
else
{
return true;
}
}
//判断人是否到达目标状态
bool isIntarget(node n1)
{
if(n1.px==tx&&n1.py==ty)
{
return true;
}
else
{
return false;
}
}
【10月12日后记】
1.如果你在第1列,木乃伊在第2列,这个时候木乃伊就要向左走,如果它左边恰好有一个墙,那它为了【只有先达到同一列才能向你走来】这个规则,只能一辈子跟那个墙硬怼,永远都出不来,你只需要静静的广搜就好。
2.写的时候出了个bug,在木乃伊抓到人之后,我直接返回false了,这是不对的,应该等所有节点都搜索完了,没一条路能返回true的时候,才能返回false。