hdu 1429 胜利大逃亡(续) bfs+状态压缩

胜利大逃亡(续)

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 8425    Accepted Submission(s): 3022


Problem Description
Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)……

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

Input
每组测试数据的第一行有三个整数n,m,t(2<=n,m<=20,t>0)。接下来的n行m列为地牢的地图,其中包括:

. 代表路
* 代表墙
@ 代表Ignatius的起始位置
^ 代表地牢的出口
A-J 代表带锁的门,对应的钥匙分别为a-j
a-j 代表钥匙,对应的门分别为A-J

每组测试数据之间有一个空行。
 

Output
针对每组测试数据,如果可以成功逃亡,请输出需要多少分钟才能离开,如果不能则输出-1。
 

Sample Input
  
  
4 5 17 @A.B. a*.*. *..*^ c..b* 4 5 16 @A.B. a*.*. *..*^ c..b*
 

Sample Output
  
  
16 -1  
    思路:之前一直按着普通的搜索来敲代码来着,后来敲到一半猛然发现走过的路貌似还能重新走,即在拥有钥匙状态不同的情况下,所走过的路还可以重新走。题中一共有10把钥匙,而每种钥匙都有两种状态,则一共有2^10种状态,所以就开了一个三维数组vis[25][25][1<<10]。数组最后一维用一个整数表示一个十位的二进制数来记录所拥有钥匙的状态值,如1=0 000 000 001,表示拥有一把a钥匙。用与(&)运算来判断是否有钥匙,或运算(|)捡起钥匙。如flag为当前拥有钥匙的状态:
 flag=0 000 000 000;表示一把钥匙都没;
flag=flag|(1<<(map[x][y]-'a')) =0 000 000 000|0 000 000 001=0 000 000 001,表示捡起一把a钥匙
flag&(1<<(map[x][y]-'A'))=0 000 000 001&0 000 000 001=0 000 000 001=1,表示拥有一把a钥匙
代码:
#include<stdio.h>
#include<string.h> 
#include<queue>
#include<string.h>
#include<ctype.h>
using namespace std;
int map[25][25];
int vis[25][25][1<<10]; 
int Sx,Sy;
int n,m,t;
int v[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//上下左右 
struct Node
{
	int x,y;
	int step;
	int flag;//表示一个10位的二进制数,用来记录当前有哪几把钥匙 
	Node(int _x,int _y,int _step,int _flag):x(_x),y(_y),step(_step),flag(_flag){}
};
int bfs()
{ 
	queue<Node> q;
	q.push(Node(Sx,Sy,0,0));
	vis[Sx][Sy][0]=1; 
	int x,y,step,flag;
	while(!q.empty())
	{
		Node node=q.front();
	    x=node.x;
	    y=node.y; 
	    q.pop();
	    if(node.step>=t-1) break;//未在规定时间内逃出 t-1是因为要在魔王回来之前离开 
	    for(int i=0;i<4;i++)
	    {
	       int xx=x+v[i][0];
		   int yy=y+v[i][1];
		   step=node.step+1;//步数加1 
	       flag=node.flag;	
	       if(xx<0||xx>=n||yy<0||yy>=m||map[xx][yy]=='*'||vis[xx][yy][flag]) continue;//越界,遇墙,之前到过的状态则后退 
	       if(isupper(map[xx][yy])&&!(flag&(1<<(map[xx][yy]-'A'))))
	       {
	       	 continue;//判断是否为门,并查看是否有钥匙能打开,isupper()判断是否为大小写字母
		   } 
		    
		   if(islower(map[xx][yy])) //若为钥匙,则捡起 ,islower()判断是否为小写字母 
		   {
		   	 flag|=(1<<(map[xx][yy]-'a')); 
		   }
		   if(map[xx][yy]=='^')//若到出口,则返回当前所用步数 
		   {
		   	 return step;
		   }
		   if(!vis[xx][yy][flag]) //相同状态下,已走过的不再走 
		   {
		   	 vis[xx][yy][flag]=1;//记录当前状态 
		   	 q.push(Node(xx,yy,step,flag));
		   }
		}
	}
	return -1;//逃出失败 
}
int main()
{

	while(scanf("%d %d %d",&n,&m,&t)!=EOF)
	{ 
		getchar();
		memset(vis,0,sizeof(vis));//初始化标志数组 
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				scanf("%c",&map[i][j]);
				if(map[i][j]=='@') Sx=i,Sy=j;
			}
			getchar();
		} 
        printf("%d\n",bfs());
	}
	
} 





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值