迷宫
【问题描述】 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");//华丽地结束,搜不到 }
来源:https://www.cnblogs.com/baikou/p/12204073.html