864 获取所有钥匙的最短路径

题目描述:
给定一个二维网格 grid。 “.” 代表一个空房间, “#” 代表一堵墙, “@” 是起点,(“a”, “b”, …)代表钥匙,(“A”, “B”, …)代表锁。
我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
假设 K 为钥匙/锁的个数,且满足 1 <= K <= 6,字母表中的前 K 个字母在网格中都有自己对应的一个小写和一个大写字母。换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。
返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。

示例 1:
输入:["@.a.#","###.#",“b.A.B”]
输出:8

示例 2:
输入:["@…aA","…B#.","…b"]
输出:6

提示:
1 <= grid.length <= 30
1 <= grid[0].length <= 30
grid[i][j] 只含有 ‘.’, ‘#’, ‘@’, ‘a’-‘f’ 以及 ‘A’-‘F’
钥匙的数目范围是 [1, 6],每个钥匙都对应一个不同的字母,正好打开一个对应的锁。

方法1:
主要思路:解题汇总链接
(1)使用广度优先搜索,配合状态压缩;
(2)先遍历原数组,找出grid中的起始点位置和总的钥匙的个数,并将起始点的位置压入到队列中;
(3)队列中元素存储位置和当前位置的状态,由于钥匙最多只有6个,则钥匙的状态最多只有64个,故可以使用状态压缩,来保存各个钥匙在某个位置时,获得的状态;
(4)为了记录之前出现过的位置的状态,使用三维数组来保存从初始位置到各个位置的距离,数组初始置为-1,来标识没有访问过当前位置;

class Solution {
public:
    int shortestPathAllKeys(vector<string>& grid) {
		int rows = grid.size(), cols = grid[0].size();
		int count_k = 0;
		queue<vector<int>> q;
		//遍历原数组,找出钥匙的个数和初始的位置
		for (int i = 0; i < rows; ++i) {
			for (int j = 0; j < cols; ++j) {
				if (grid[i][j] >= 'a'&&grid[i][j] <= 'z') {
					++count_k;
				}
				else if (grid[i][j] == '@') {
					q.push({ i,j ,0});
				}
			}
		}
		//各个方向
		vector<vector<int>> next = { {1,0},{-1,0},{0,1},{0,-1} };
		//三维数组,记录到各个位置的距离
		vector<vector<vector<int>>> distance(30, vector<vector<int>>(30, vector<int>(64, -1)));
        distance[q.front()[0]][q.front()[1]][0]=0;//初始化位置的距离
		int target = (1 << count_k) - 1;//最终所有的钥匙都要找到,既终止位置的状态
		while (!q.empty()) {//终止队列为空
			vector<int> cur_pos = q.front();//当前位置
			q.pop();
			for (int i = 0; i < next.size(); ++i) {//各个方向
				int next_r = cur_pos[0] + next[i][0];
				int next_c = cur_pos[1] + next[i][1];
				if (next_r >= 0 && next_r < rows&&next_c >= 0 && next_c < cols && grid[next_r][next_c] != '#') {//当前方向的位置可能访问
					if (grid[next_r][next_c] >= 'A'&&grid[next_r][next_c] <= 'Z'
                        &&!(cur_pos[2]&(1<<(grid[next_r][next_c] - 'A')))) {//有锁,但没有当前锁的钥匙
						continue;
					}
					int cur_state = cur_pos[2];//当前获得的钥匙状态
					if (grid[next_r][next_c] >= 'a'&&grid[next_r][next_c] <= 'z') {//当前方向的位置是钥匙
						cur_state |= 1 << (grid[next_r][next_c] - 'a');//将当前钥匙的状态加入
						if (cur_state == target) {//到达终止位置
							return distance[cur_pos[0]][cur_pos[1]][cur_pos[2]] + 1;
						}
					}
					//当前位置之前没有访问过,则加入当前位置
					if (distance[next_r][next_c][cur_state] == -1) {
						distance[next_r][next_c][cur_state] = distance[cur_pos[0]][cur_pos[1]][cur_pos[2]] + 1;
						q.push({ next_r ,next_c ,cur_state });
					}
				}
			}
		}
		return -1;
	}
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值