获取所有钥匙的最短路径(二进制状态压缩)
description
给定一个二维网格 grid。
“.” 代表一个空房间, “#” 代表一堵墙, “@” 是起点,(”a”, “b”, …)代表钥匙,(”A”, “B”, …)代表锁。
我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。
如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
假设 K 为钥匙/锁的个数,且满足 1 <= K <= 6,字母表中的前 K 个字母在网格中都有自己对应的一个小写和一个大写字母。
换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。
另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。
输出获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。
方法
1.
- 用一个数代表题目给出的钥匙数目($1<=k<=6$)6个二进制位表示每个钥匙存在(bfs结束的条件),然后bfs跑一遍图,每个点有多个状态,(不仅 包括当前点钥匙,还有以前的拿的钥匙)
所以每个点存step 和当前手里拥有的 key ,标记经过的状态vis[x][y][key],走过就不在入队了 - 每个点是否有钥匙拿,是否有钥匙解锁,墙不能走,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61#include<iostream>
#include<queue>
using namespace std;
struct node{
int sta;
int x;
int y;
int step;
node(int a,int b,int c,int d){
sta=a;x=b;y=c;step=d;
}
};
int n,m;
int sx,sy;
int ansstates=0;
int next1[4][2]={0,1,1,0,0,-1,-1,0};
bool vis[35][35][65];// 2^6==64
char grid[35][35];
int bfs(){
queue<node> que;
que.push(node(0,sx,sy,0));
vis[sx][sy][0]=1;//标记当前点拥有第0个钥匙的状态走过
while(!que.empty()){
node head=que.front();
que.pop();
for(int i=0;i<4;i++){
int sta=head.sta,x=head.x,y=head.y,step=head.step;
int tx=x+next1[i][0];
int ty=y+next1[i][1];
if( tx>=1 && ty>=1 && tx<=n && ty<=m &&(grid[tx][ty]!='#')){
char c=grid[tx][ty];
if(islower(c)) sta|=1<<(c-'a'); //在原来的基础上捡
else if(isupper(c) && !(sta>>(c-'A'))&1 ) continue;//遇到们,没有钥匙,就continue
if( !vis[tx][ty][sta] ){//遇到门有钥匙,判断该点拥有的钥匙状态是否遍历过,没有就入队
//每次捡到钥匙后还要判断钥匙是否都有了
if(sta == ansstates) return step+1;
vis[tx][ty][sta]=true;
que.push(node{sta,tx,ty,step+1});
}
}
}
}
return -1;
}
int main(){
freopen("a.txt","r",stdin);
cin>>n>>m;getchar();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%c",&grid[i][j]);
if( grid[i][j]=='@' ) sx=i,sy=j;
if( grid[i][j]>='a'&&grid[i][j]<='f') ansstates=max(ansstates,ansstates|1<<(grid[i][j]-'a') );
}
getchar();
}
cout<<bfs()<<endl;
return 0;
}
- 每个点是否有钥匙拿,是否有钥匙解锁,墙不能走,
2.(大佬教我的)用hash的思想,表示每个点拥有不同的钥匙的状态,
- 核心没变,每个点拥有的钥匙状态还是二进制压缩,只是判重变了.
- key[]用来记录题目给出的钥匙,ha[hashnum]代表每个点拥有的钥匙的不同状态是否遍历过,(hashnum为hash函数映射出来的值)
- hashnum只要放缩成不重复的就行(随意放缩)
- 这个方法每一步拿自己的钥匙,上一种解法(当前点拿下一步的钥匙)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81#include<iostream>
#include<queue>
#include<unordered_map>
using namespace std;
struct node{
int sta;
int x,y;
int step;
node(int a,int b,int c,int d){
sta=a;x=b;y=c;step=d;
}
};
int next1[4][2]={0,1,1,0,0,-1,-1,0};
int n,m;
char grid[35][35];
// bool vis[35][35][65];// (111111)2^6个状态
unordered_map<char,bool> key;
unordered_map<int,bool> ha;
queue<node> que;
int hashNode(node x){
int res=0;
res+=(x.x<<15);
res+=(x.y<<10);
res+=x.sta;
return res;
}
bool check(node x){
for(int i='a';i<='f';i++){
if(key[i]){
if( x.sta & 1<<(i-'a') ) continue;
else return false;
}
}
return true;
}
int bfs(){
while(!que.empty()){
node head=que.front();
que.pop();
//判断该点钥匙状态是否走过
int hashnum=hashNode(head);
if(ha[hashnum]) continue;
else ha[hashnum]=true;
char c=grid[head.x][head.y];
if( c<='f' && c>='a') head.sta|=1<<(c-'a');//捡钥匙
if( check( head ) ) return head.step;
//该点是锁
if( c<='F' && c>='A')
if(!head.sta & 1<<(c-'A')) continue;//跳过
for(int i=0;i<4;i++){
int tx=head.x+next1[i][0];
int ty=head.y+next1[i][1];
if( tx>=1 && ty>=1 && tx<=n && ty<=m && (grid[tx][ty]!='#')){
que.push(node{head.sta,tx,ty,head.step+1});
}
}
}
return -1;
}
int main(){
freopen("a.txt","r",stdin);
cin>>n>>m;getchar();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
grid[i][j]=getchar();
if(grid[i][j]=='@') que.push( node{0,i,j,0} );
if(grid[i][j]<='f' && grid[i][j]>='a')
key[grid[i][j]]=true;
}
getchar();
}
cout<<bfs();
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!