【状态压缩】【广度优先搜索】迷宫游戏

【题目描述】
Pluto 已经很久没有玩游戏了,今天 Pluto 难得能够歇一天。于是,Pluto 决定要玩一个非常好玩的迷宫游戏。这个迷宫游戏在一个 n 行 m 列的矩阵上进行,游戏中的任意时刻不能走出这个矩阵。在这个矩阵中,有下列几种元素:

. :表示一块平地。

X :表示一堵墙,不能经过。

S :表示游戏的起点,有且仅有一个。

T :表示游戏的终点,有且仅有一个。

A~H :表示一种类型的门,每种类型的门最多只会出现一次。

a~h :表示每种类型的门对应的钥匙,每种类型的钥匙最多只会出现一次,并且保证只会与对应类型的门同时出现。

在这个游戏中,玩家从起点开始,每次向上下左右中的一个方向移动一格,若发生下列任意一种或多种情形,则该步移动不合法,游戏退出。

1 . 走出矩阵。

2 . 移动后的格子中是墙。

3 . 移动后的格子中是门,且还未获得对应的钥匙。

若玩家走到一个有钥匙的格子中,则玩家将获得格子中的钥匙(注意:玩家不能选择放弃获取。)最终,若玩家到达终点,则游戏完成。

现在,Pluto 希望你能帮助他在完成游戏的同时,获得尽量少的钥匙。如果有多解,请输出任意一解。
【输出描述】
一共若干字符,表示你的操作序列。‘U’表示向上,‘D’表示向下,‘L’表示向 左,‘R’表示向右。(注意:输出的操作序列需保证长度不超过 10000000。
【样例输入】
3 4
S.a.
.X.X
ATX.
【样例输出】
RRLLDDR

【状态压缩的原理】

状态压缩常用于不好表示状态的dp和类似这道题的搜索,一般特点是题目中某一维度数据规模较小。状态压缩基于二进制的位运算,利用二进制中的0和1来表示状态。一般常用两种位运算:
|(按位或运算):对于两个数a,b,二进制表示法a,b中的每一位上有1则为1,否则为零。
eg.
a=101001010;
b=001000110;
c=101001110;(c=a|b)
&(按位与运算):对于两个数a,b,二进制表示法a,b中的每一位,都是1则为1,否则为0。
eg.
a=101001010;
b=001000110;
c=001000010;(c=a&b)

【大体思路】

基于以上操作,我们可以用一个int来存储当前钥匙有无的状态。1表示有,0表示无,从低位到高位依次表示钥匙a,钥匙b,钥匙c…那么我们可以利用位运算来实现判断钥匙和添加钥匙。同时,由于同一个格子可能重复走,每次经过钥匙状态不同,因此我们需要一个三维的vis数组来当做广度优先搜索的判重数组,前两位表示坐标,第三维表示钥匙状态,因此我们就能得到如下算法:

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#define re register
using namespace std;
int n,m; 
int dx[4]={0,-1,0,1};
int dy[4]={-1,0,1,0};
char mp[201][201];
struct node{
	int x,y,now;
	int num;
	int l;
}k,u;
int a,b;
bool vis[201][201][129];
struct nde{
	int a,b,c;
}pre[201][201][129];//记录当前状态由哪一个格子转移过来,便于打印方案。 
int sx,sy;
inline void pree(node &u)//找到终点和起点 
{
	for(int re i=1;i<=n;i++)
		for(int re j=1;j<=m;j++)
			if(mp[i][j]=='S')sx=u.x=i,sy=u.y=j;
			else if(mp[i][j]=='T')a=i,b=j;
	u.num=0;
	u.now=0;
}
int yy;
inline bool check(node &k,int &i)//方案合法判断 
{
	k.x=k.x+dx[i];
	k.y=k.y+dy[i];
	if(vis[k.x][k.y][k.now])return 0;
	if(k.x<=0||k.y<=0||k.x>n||k.y>m)return 0;
	if(mp[k.x][k.y]=='S')
	{
		vis[k.x][k.y][k.now]=1;return 1;
	}
	if(mp[k.x][k.y]=='T')
	{
		vis[k.x][k.y][k.now]=1;
		return 1;
	}
	if(mp[k.x][k.y]=='.')
	{
		vis[k.x][k.y][k.now]=1;
		return 1;
	}
	else
	{
		if('a'<=mp[k.x][k.y]&&mp[k.x][k.y]<='z')
		{
			k.now=k.now|(1<<(mp[k.x][k.y]-'a'));
			vis[k.x][k.y][k.now]=1;
			return 1;
		}
		else
		{
			if(k.now&(1<<(mp[k.x][k.y]-'A')))
			{
				vis[k.x][k.y][k.now]=1;return 1;
			}
		}
	}
	return 0;
}
char ans[50001];
inline void print(int x,int y,int s){//方案打印 
	int prex,prey,prenow,cnt=0;
	while(x!=sx||y!=sy||s){
		nde tmp=pre[x][y][s];
		prex=tmp.a,prey=tmp.b,prenow=tmp.c;
		if(prex<x)ans[++cnt]='D';
		else if(prex>x)ans[++cnt]='U';
		else if(prey<y)ans[++cnt]='R';
		else ans[++cnt]='L';
		x=prex,y=prey,s=prenow;
	}
	for(int i=cnt;i>0;i--)putchar(ans[i]);
}
inline void bfs()
{
	pree(u);
	queue<node>q;
	q.push(u);
	while(!q.empty())
	{
		node l=q.front();q.pop();
		if(l.x==a&&l.y==b)
		{
			print(l.x,l.y,l.now);
			return;
		}
		for(int re i=0;i<4;i++)
		{
			k=l;
			if(check(k,i))
				q.push(k),pre[k.x][k.y][k.now]=(nde){l.x,l.y,l.now};
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int re i=1;i<=n;i++)
		scanf("%s",mp[i]+1);
	bfs();
}

是不是超级友好的一道题?然而我调试了一个小时。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值