bfs加状态压缩

本文介绍了一个基于状态压缩的广度优先搜索(BFS)算法来解决包含钥匙和门的迷宫问题。 Ignatius试图从起点到达出口,避开魔王的视察。通过将每个位置的钥匙状态编码到二进制中并使用BFS搜索,可以判断是否能够成功逃离地牢并在规定时间内到达出口。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

        这是道迷宫题,迷宫一般用bfs,这道题的难点在于每个点的状态难以表示出来,如果用普通的vis数组,就要开12维(10把钥匙和x,y),无论是看起来还是写起来都很烦,那么怎么表示每个点的状态呢,就要用到状态压缩。

        何为状态压缩,就是将每个点有无哪把钥匙压缩到10个 二进制上(从低位到高位1表示有该钥匙,0代表无该钥匙),所以这时候的vis数组为【x】【y】【1<<]】,解决了这个难点直接bfs即可,值得一提的是,当某个点能走时要在把它放入队列的同时标记,如果先放入队列,拿出来时再标记是会tle的。


#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int n,m,T;



char a[30][30];

bool vis[25][25][1<<11];                     //一二维是行和列,第三维是是否有钥匙的状态,用10个二进制位表示,1位有,0为无//如果用int存会爆掉
struct point{
	int x,y,time,state;                     //单次状态可以用int存 就是state
	point(int a,int b,int c,int d)          //构造函数
	{
		x=a;y=b;state=c;time=d;
	}
	point(){               
	}
};

int dx[4]={0,1,0,-1};                      //用两个数组的配合表示四个方向的移动
int dy[4]={1,0,-1,0};



int bfs(point start,point end)             //将起点和终点传入广搜
{
	queue <point> que;
	que.push(start);
	vis[start.x ][start.y ][start.state ]=1;
	while(!que.empty()){
		point tp=que.front();                  //tp是当前的状态
		que.pop();
		if(tp.x==end.x&&tp.y==end.y)
		return tp.time;
		
		
		for(int i=0;i<4;i++){
			int x=tp.x+dx[i];                  //此时的xyst是下一步的状态参数
			int y=tp.y+dy[i];
			int s=tp.state;
			int t=tp.time;
			
			
			
			
			
	if(vis[x][y][s]==0&&a[x][y]!='*'){    //下一步没走过且不是墙
		vis[x][y][s]=1;
		if(a[x][y]!='.'){                 //下一步不是点,就只有钥匙和门两种情况
			if(a[x][y]>='a'){
			s=s|(1<<(a[x][y]-'a'));   //如果是钥,标记,并且塞进队列
			vis[x][y][s]=1;
			que.push(point(x,y,s,t+1));
			}
			else
					{
	if(s&(1<<(a[x][y]-'A'))){   //如果是门,就判断有没有钥匙,用and实现该二进制位
							que.push(point(x,y,s,t+1));
						}
					}
				}
		else{
			que.push(point(x,y,s,t+1));    //如果下一步是路,就可以直接走
				}
			}
		}
		
	}
	return -1;
}





int main()
{
	while(cin>>n>>m>>T)
	{
		memset(vis,0,sizeof(vis));
		for(int i=0;i<=n+1;i++)
			for(int j=0;j<=m+1;j++)
			{
				a[i][j]='*';              //在外围包一层墙
			}
		point s,e;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				cin>>a[i][j];
				if(a[i][j]=='@')
				{
					s=point(i,j,0,0);    //起点
					a[i][j]='.';
				}
				if(a[i][j]=='^')
				{
					e=point(i,j,0,0);    //终点
					a[i][j]='.';
				}
			}
			int ans=bfs(s,e);           //开始广搜
            if(ans==-1||ans>=T)
            {
            	cout<<"-1"<<endl;
			}
			else{
				cout<<ans<<endl;
			}

	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值