迷宫
【问题描述】
n*m 的迷宫, 迷宫中有 k 种钥匙, 每个格子有一个整数 x, 如果 x=0 说明可
以任意到达, |x|>k 代表不允许到达。 x>0 代表该位置有第 x 种钥匙, 捡起地上的
钥匙需要花费 1 步。 x<0 代表到达该位置需要第-x 种钥匙, 开门不需要时间, 问
从左上角走到右下角最少需要多少步? (数据保证左上角是 0)
【输入格式】
第一行三个正整数 n,m,k
接下来 n 行, 每行 m 个整数表示地图
【输出格式】
输出一个整数, 表示从左上角走到右下角最少需要多少步, 若不能到达输出-1.
【样例输入】
3 5 2
0 0 0 0 0
2 3 0 0 -1
3 1 -2 3 0
【样例输出】
14
【数据解释】
先拿到第 2 把钥匙, 然后再拿第 3 把钥匙
【数据规模与约定】
20% n,m<=10 k<=2
另 30% n,m<=1000 k=0
100% n,m<=1000 k<=5
代码及解析
#include<queue>
#include<cstdio>
#include<algorithm>
#define N 1005
using namespace std;
int n,m,k;
int map[N][N];
struct node{int x,y,s,dis;};
queue<node>q;
bool vis[N][N][32];//第三维有2^5种可能性(最多5把钥匙)
int dx[]={0,0,-1,1};
int dy[]={1,-1,0,0};
int main()
{
// freopen("maze.in","r",stdin);
// freopen("maze.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&map[i][j]);//存图
q.push((node){1,1,0,0});//依次为坐标x,y,钥匙状态,步数
while(!q.empty())//bfs开始
{
node tmp=q.front();q.pop();
if(tmp.x==n&&tmp.y==m)//找到即结束程序
{
printf("%d
",tmp.dis);
return 0;
}
if(map[tmp.x][tmp.y]>0)//有钥匙 ,则会有第五种操作(当然不会是死路,死路不会入队 ……45行)
{
int s=tmp.s|(1<<(map[tmp.x][tmp.y]-1));
//这一步神来之笔, 1<<(map[tmp.x][tmp.y]-1)表示钥匙的状态 与当前钥匙取并集
if(!vis[tmp.x][tmp.y][s])//如果这个状态还没有过
{
vis[tmp.x][tmp.y][s]=1;
q.push((node){tmp.x,tmp.y,s,tmp.dis+1});//把这个状态更新到队列中,步数+1
}
}
for(int z=0;z<4;z++)
{
int x=tmp.x+dx[z];
int y=tmp.y+dy[z];
int s=tmp.s;
if(x<1||x>n||y<1||y>m)continue;//出图不入队
if(map[x][y]>k)continue;//死路不入队
if(map[x][y]<0&&!((1<<(-map[x][y]-1))&s))continue;
//注意这个地方是负数,要取相反数
//还没拿到钥匙的不入队 ,拿到钥匙了就直接入队了,不消耗钥匙,所以不必单判
if(!vis[x][y][s])
//这个位置还没走过的话入队(以前可能经过过这个点,但这次我拿钥匙来了也算没来过,入队)
{
vis[x][y][s]=1;
q.push((node){x,y,s,tmp.dis+1});
}
}
}
puts("-1");//华丽地结束,搜不到
}