题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1429
******昨天刚看了个状态压缩的题,原来对这个不太了解,今天复习搜索遇到这个
题目,很快就有了思路,迷宫里面有十种钥匙(从a到j),随意走到每个位置
我们可能拥有不同的钥匙数量,即使钥匙数量相同,钥匙的类型也不一定全
部相同,对于十种钥匙,其组合情况有2^10方种组合,这是每个位置可能有
的状态数,因此我们开辟标记数组vis[22][22][1024]来标记可能出现的所有
状态,对于每一个节点,我们维护的成员有该节点的位置(x,y)到达该节点
的时间(time),和该节点所拥有的钥匙状态(state)。
思路如下:
******当我们走到有钥匙的节点时,首先我们就要看该钥匙我们是否已经有了,
这个通过与运算实现,如果有了,当前钥匙状态和上一步的钥匙状态是一样的,
如果没有该钥匙,需要加上该钥匙构成新的状态。
******当我们走到需要钥匙开锁的节点时,就看当前是否有这种钥匙,如果有
则普通广搜,我们直接走就行,否则就不能走
******走到星号位置,普通广搜下去。
******走到某个位置,我们都有一个状态,如果这种状态还没遇到过,OK,我们
对其标记然后入队,假如这种状态遇到过了,我们需要比较时间,如果这次时间
更短的话,该节点也需要入队。
AC代码:
#include <iostream>
#include <stdio.h>
#include <queue>
#include <math.h>
#include <string.h>
using namespace std;
char Map[25][25];
int vis[22][22][1024]; ///10种钥匙,总共能组成2^10次方种状态,vis[x][y][state]里面存放这种状态的时间
int sx,sy,ex,ey;
int n,m,t; ///n行,m列
int dir[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};
struct Node
{
int x,y,time;
int state;
friend bool operator<(Node a,Node b)
{
return a.time>b.time;
}
};
void bfs()
{
memset(vis,-1,sizeof(vis));
Node cur,nex;
cur.time = 0;
cur.x = sx;
cur.y = sy;
cur.state = 0; ///什么钥匙都没有
vis[cur.x][cur.y][cur.state] = 0;
priority_queue<Node>qu;
qu.push(cur);
while(!qu.empty())
{
cur = qu.top();
qu.pop();
if(cur.time>=t)
continue;
if(cur.x==ex && cur.y==ey && cur.time<t)
{
printf("%d\n",cur.time);
return;
}
for(int i = 0; i < 4; i++)
{
nex.x = cur.x + dir[i][0];
nex.y = cur.y + dir[i][1];
nex.time = cur.time + 1;
if(nex.x>0&&nex.x<=n&&nex.y>0&&nex.y<=m&&Map[nex.x][nex.y]!='*')
{
///到了有钥匙的地方
if(Map[nex.x][nex.y]>='a' && Map[nex.x][nex.y]<='j')
{
int num = Map[nex.x][nex.y]-'a';
num = (1<<num);
///如果已经有这种钥匙了
if((cur.state&num) == num)
{
nex.state = cur.state; ///不能再加上该钥匙。
}
else
{
nex.state = cur.state + num; ///否则加上这种钥匙
}
if(vis[nex.x][nex.y][nex.state] == -1) ///这种状态没遇到过
{
vis[nex.x][nex.y][nex.state] = nex.time; ///留下到达这种状态的时间
}
else if(nex.time < vis[nex.x][nex.y][nex.state])
{
///这种状态遇到过,如果这次时间更短,也需要入队,并更新vis
vis[nex.x][nex.y][nex.state] = nex.time;
qu.push(nex);
}
}
else if(Map[nex.x][nex.y]>='A' && Map[nex.x][nex.y]<='J')
{
///到达有锁的地方,有钥匙的话就走,否则该点不能走
int num = Map[nex.x][nex.y]-'A';
num = (1<<num);
if((cur.state&num) == num) ///有这种钥匙就可以走这个位置
{
nex.state = cur.state;
if(vis[nex.x][nex.y][nex.state] == -1)
{
vis[nex.x][nex.y][nex.state] = nex.time;
qu.push(nex);
}
else if(nex.time < vis[nex.x][nex.y][nex.state])
{
vis[nex.x][nex.y][nex.state] = nex.time;
qu.push(nex);
}
}
}
else ///是星号就是普通广搜
{
nex.state = cur.state;
if(vis[nex.x][nex.y][nex.state] == -1)
{
vis[nex.x][nex.y][nex.state] = nex.time;
qu.push(nex);
}
else if(nex.time<vis[nex.x][nex.y][nex.state])
{
vis[nex.x][nex.y][nex.state] = nex.time;
qu.push(nex);
}
}
}
}
}
printf("-1\n");
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&t))
{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
scanf(" %c",&Map[i][j]);
if(Map[i][j] == '@')
{
sx = i;
sy = j;
}
if(Map[i][j] == '^')
{
ex = i;
ey = j;
}
}
bfs();
}
return 0;
}