【NOJ1147】【算法实验三】【分支限界法】木乃伊迷宫

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。

    原文作者:分支限界法
    原文地址: https://blog.csdn.net/qq_41727666/article/details/83029584
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞