【广搜】胜利大逃亡 escape

  胜利大逃亡

escape.pas/c/cpp

1S/256MB

【题目描述】

elfness被魔王抓走了,这次魔王把elfness关在一个n*m的地牢里。地牢的某个地方安装了一个带锁的门,钥匙藏在地牢的另外一个地方,elfness想要通过这个门,就必须先走到藏钥匙的地方取钥匙。刚开始的时候elfness被关在(sx,sy)的位置,而离开地牢的门在(ex,ey)的位置。elfness每分钟只能从一个位置走到相邻四个位置中的其中一个。魔王每t分钟都回地牢视察一次,若发现elfness不在原位置便会把他拎回去。经过若干次的尝试,elfness已经画出了整个地牢的地图。现在请你帮他计算能否再次成功逃亡。只要在魔王下次视察之前走到出口就算离开地牢,如果魔王回来的时候还未到出口都算逃亡失败。

【输入】

输入文件:escape.in

第一行有三个整数n,m,t。接下来的n行m列为地牢的地图,其中包括:

. 代表路

* 代表墙

@ 代表elfness的起始位置

^ 代表地牢的出口

A 代表带锁的门

a 代表钥匙

【输出】

输出文件:escape.out

输出为一行,包含一个整数。对于可以成功逃亡的情况,请输出至少需要多少分钟才能离开,如果不能则输出-1。

【输入样例】

4 4 100
@..A
a.*.
***.
^...

【输出样例】

11

【数据范围】

  对于100%的数据,2<=n,m<=20,t>0








这一题给人的第一印象就是和走迷宫如出一辙,然后深搜!


但是我们看看时间复杂度,极限情况一共有20*20=400个格子,而深搜的效率是O(2n),所以肯定超时了!!!

所以这个时候我们就应该选择宽搜的方法


对比走迷宫,我们这道题多了一个钥匙的状态,这个简单,一个key变量就可以记录下来!


走迷宫时为了提高搜索效率,我们在入队的时候会进行判重,如果当前点已经走过了,就不会再走了

但是这一题会出现这样一种情况,如下图

@.*.
*.A^
.a*.

很明显,最短的路径应该是(0,0)-->(0,1)-->(1,1)-->(2,1)-->(1,1)-->(1,2)-->(1,3)就ok了,但是看看,我们为了去拿钥匙,不得不重复走了(1,1),那么这个应该如何来判重呢?


我们发现,一个点最多只能走两次,并且是没有钥匙的走一次,有钥匙的走一次,这样才会使答案最优!


这样我们就可以在原来的判重hash上加一维,来表示钥匙的状态,h[x][y][0]表示 没有钥匙时(x,y)是否走过 , h[x][y][1] 表示  有钥匙时(x,y)是否走过。  这样就可以轻松解决上面的问题了



C++ Code

/*
C++ Code
http://blog.csdn.net/jiangzh7
By Jiangzh
*/
#include<cstdio>
#include<queue>
using namespace std;

const int MAXN=50;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};

int n,m,T;
char map[MAXN][MAXN];
struct status{int x,y,key,step;};
status first;
int h[MAXN][MAXN][2];
int tx,ty;
queue<status> q;

void read()
{
	freopen("escape.in","r",stdin);
	freopen("escape.out","w",stdout);
	
	scanf("%d%d%d\n",&n,&m,&T);
	for(int i=0;i<n;i++)
	{
		scanf("%s\n",map[i]);
		for(int j=0;j<m;j++)
		{
			if(map[i][j]=='@') {first.x=i;first.y=j;}
			if(map[i][j]=='^') {tx=i;ty=j;}
		}
	}
}

status change(status node,int k)
{
	node.x+=dx[k];
	node.y+=dy[k];
	node.step++;
	if(node.x>=0&&node.x<n&&node.y>=0&&node.y<m)
	    if(map[node.x][node.y]=='a')
	        node.key=1;
	return node;
}

bool check(status node)
{
	int x=node.x,y=node.y;
	if(x<0 || x>=n || y<0 || y>=m) return false;
	if(map[x][y]=='*') return false;
	if(map[x][y]=='A' && !node.key) return false;
	if(h[x][y][node.key]) return false;
	return true;
}

void work()
{
	q.push(first);
	status tmp,newtmp;
	while(!q.empty())
	{
		tmp=q.front();q.pop();
		for(int k=0;k<4;k++)
		{
			newtmp=change(tmp,k);
			if(check(newtmp))
			{
				if(newtmp.x==tx && newtmp.y==ty)
				{
					printf("%d",newtmp.step);
					exit(0);
				}
				q.push(newtmp);
				h[newtmp.x][newtmp.y][newtmp.key]=true;
			}
		}
	}
	printf("-1");
}

int main()
{
	read();
	work();
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值