E : 获取所有钥匙的最短路径
Time Limit:2 Sec Memory Limit:128 MiB
Back Submit Edit
Description
给定一个二维网格 grid。
“.” 代表一个空房间, “#” 代表一堵墙, “@” 是起点,(“a”, “b”, …)代表钥匙,(“A”, “B”, …)代表锁。
我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。
如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
假设 K 为钥匙/锁的个数,且满足 1 <= K <= 6,字母表中的前 K 个字母在网格中都有自己对应的一个小写和一个大写字母。
换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。
另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。
输出获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。
Input
整数n, m,代表grid的行、列 (1 ≤ n,m ≤ 30)
以下为n*m的矩阵
Output
获取所有钥匙所需要的移动的最少次数
Sample Input
3 5
@.a.#
###.#
b.A.B
Sample Output
8
More Info
输入:
3 5
@…aA
…B#.
…b
输出:
6
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
char grid[35][35];//用于存储地图
bool vis[35][35][65];//用true判断该位置持有的钥匙组合状态
struct State{
int keys,x,y,step;
State(int keys,int x,int y,int step):keys(keys),x(x),y(y),step(step){
}
};
int sx=-1,sy=-1;//用于记录初始位置
int toState=0;//用状态压缩存储拿到所有钥匙的状态
int Next[4][2]={0, 1, 0, -1, 1, 0, -1, 0};
int n,m;
int bfs(){
queue<State>Q;//用队列来模拟bfs搜索
Q.push(State(0,sx,sy,0));//初始状态入队
vis[sx][sy][0]=1;//记录初始状态
while(!Q.empty())
{
State head=Q.front();
Q.pop();//用head记录当前位置数据在出队
for(int k=0;k<4;k++)//通过该层循环实现上下左右移动
{
int x=head.x,y=head.y,step=head.step,keys=head.keys;
int nx=x+Next[k][0];
int ny=y+Next[k][1];
if(nx>=0&&ny>=0&&nx<n&&ny<m&&grid[nx][ny]!='#') //判断当移动后的位置在地图中并且不是墙的时候
{
if(islower(grid[nx][ny]))
keys|=1<<(grid[nx][ny]-'a');//通过状态压缩更新拿到的钥匙数
else if(isupper(grid[nx][ny])&&!(keys>>(grid[nx][ny]-'A')&1))
continue;//判断当前门有没有对应的钥匙若没有进行下一个方向的判断
if(!vis[nx][ny][keys]) //当,当前位置所拿钥匙的组合数没有出现过时进行数据更新
{
if(keys==toState)return step+1;//当往前走一步时能完成操作时,步数加一向前走一步
vis[nx][ny][keys]=true;
Q.push(State(keys,nx,ny,step+1));//当该位置还没走过时入队模拟bfs搜索
//因为这是队列所以可能会有多个可能的结果,但最小的步数一定被先搜索到
}
}
}
}
return -1;
}
int main(){
// freopen("qwe.txt","r",stdin);
cin>>n>>m;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>grid[i][j];
if(grid[i][j]=='@')
sx=i,sy=j;
if(grid[i][j]>='a'&&grid[i][j]<='f')
toState|=1<<(grid[i][j]-'a');//通过状态压缩更新持有钥匙的状态
}
}
cout<<bfs()<<endl;
return 0;
}