day 1 T1 迷宫

浪尽此生 提交于 2020-01-17 09:00:35

迷宫

【问题描述】
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\n",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");//华丽地结束,搜不到 
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!