胜利大逃亡(续)hdu1429 (状态压缩bfs)

http://acm.hdu.edu.cn/showproblem.php?pid=1429


几组测试数据:


4 5 17

@A.B.

a*.*.

*..*^

c..b*

 

4 5 111

@A.B.

.*.*.

.*.*^

cC.b*

 

1 4 11

@cC^

 

1 8 11

@abACcB^

 

2 8 111

@AABBCD^

a*b*cBd*

 

6 7 111

@aAbBC*

AAA.BC*

BBBBcC*

CCCCCC*

.******

......^

 


状态压缩就是用二进制表示状态
在搜索中有很多种状态,手中有0把钥匙,有1把钥匙,有2把钥匙......
题目中是A-J一共有10把钥匙,
有钥匙和没有钥匙一共两种情况
一共十把所以有pow(2,10)种情况也就是1024。
1024转为二进制就是1 00000 00000。
开成数组正好是0-1023 对应的二进制就是0-1111111111
有A钥匙的时候就是0000000001      ‘A’-'A'=0;1<<0=1;转为二进制就是1
有B钥匙的时候就是0000000010       'B'-'A'=1;1<<1=2; 转为2进制就是10
有C钥匙的时候就是0000000100       'C'-'A'=2;1<<2=4; 转为2进制就是100

但是当手里有手里有钥匙的时候又捡到一把该怎么办:
例如有A钥匙的时候捡到一把B钥匙:
0000000001|0000000010=0000000011;

开锁的时候:
遇到一扇B门,你手里只有一把A钥匙和C钥匙怎么判断开门:
'B'-'A'=1;
1<<1=2;
B门表示为0000000010
手里有一把A钥匙和C钥匙用二进制表示为0000000101
0000000010&0000000101=0    说明不能开门。
因为若有B钥匙结果必定不为0。
————————————————
参考:https://blog.youkuaiyun.com/qq_32454729/article/details/52087216


所以这里的还有一个点就是vis要开三维,第三维表示当前拥有的钥匙状态量,如果在当前状态走过的点则不能再走,拿都钥匙后可以重新回去。这个看原来的第一个测试数据就能看出来。   先要下去拿钥匙再回去,所以在模板上更改。第三维的大小看2进制有多少种情况来定


#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
//时间限制 状态压缩 在一定条件下可以回路 
//每一个结构体的now都有next对应 
int vis[24][24][1026];
char ditu[25][25];
int sx,sy,fx,fy;
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
struct node{
	int x;
	int y;
	int key;
	int output;
};
int n,m,t;
int k;
int bfs()
{
	queue<node> que;
	node now,next;
	now.x=sx,now.y=sy,now.key=0;now.output=0;
	que.push(now);
	vis[sx][sy][0]=1;
	while(que.size())
	{
		now=que.front();que.pop();
		
		if(now.x==fx&&now.y==fy)
		{
		 	return now.output;                   ///为了方便进行考虑达不到终点的情况,对这里进行模板上的一点修改 
		}
		for(int i=0;i<4;i++)
		{
			int nx=now.x+dx[i];int ny=now.y+dy[i];int nkey=now.key;int nouput=now.output;
			if(nx>=0&&nx<n&&ny>=0&&ny<m&&ditu[nx][ny]!='*'&&vis[nx][ny][nkey]==-1)
			{
				next.x=nx;next.y=ny;next.key=nkey;next.output=nouput+1;
				//vis的第三维的意思是在目前的状态钥匙下是否走过该点,如果走过就不能再走,如果拿到钥匙刷新成下一个状态,就能走 
				if(ditu[nx][ny]>='a'&&ditu[nx][ny]<='j')   //增加钥匙 
				{
					int temp=ditu[nx][ny]-'a';    
				    next.key=(nkey|(1<<temp) );
				    
				}
				if(ditu[nx][ny]>='A'&&ditu[nx][ny]<='J')
				{
					int temp=ditu[nx][ny]-'A';
					if( (nkey&(1<<temp) )==0)     注意括号!!!! 
					continue;	
		 
				}
				vis[nx][ny][next.key]=1;
				que.push(next);
			}
			
		}
		
	}
	///注意不能到的情况 
return -1;	
}
int main(void)
{
	while(scanf("%d%d%d",&n,&m,&t)!=EOF)
	{
			memset(ditu,'\0',sizeof(ditu));memset(vis,-1,sizeof(vis));
			for(int i=0;i<n;i++)
			cin>>ditu[i];
			
			for(int i=0;i<n;i++)
			{
				for(int j=0;j<m;j++)
				{
					if(ditu[i][j]=='@')
					sx=i,sy=j;
					if(ditu[i][j]=='^')
					fx=i,fy=j;	
				}	
			}
			int res=bfs();
			if(res>=t)
			printf("-1\n");
			else if(res<t)
			printf("%d\n",res); 
	}
return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值