【题目描述】
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();
}
是不是超级友好的一道题?然而我调试了一个小时。