首页 > 其他分享 >[leetcode每日一题]11.10

[leetcode每日一题]11.10

时间:2022-11-10 15:37:03浏览次数:48  
标签:f1 emplace min int 每日 11.10 grid key leetcode

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

给定一个二维网格 ​​grid​​ ,其中:

  • '.' 代表一个空房间
  • '#' 代表一堵
  • '@'
  • 小写字母代表钥匙
  • 大写字母代表锁

我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。

假设 k 为 钥匙/锁 的个数,且满足 ​​1 <= k <= 6​​,字母表中的前 ​​k​​ 个字母在网格中都有自己对应的一个小写和一个大写字母。换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。

返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 ​​-1​​ 。

示例 1:

[leetcode每日一题]11.10_BFS

输入:grid = ["@.a.#","###.#","b.A.B"]
输出:8
解释:目标是获得所有钥匙,而不是打开所有锁。

示例 2:

[leetcode每日一题]11.10_BFS_02

输入:grid = ["@..aA","..B#.","....b"]
输出:6

示例 3:

[leetcode每日一题]11.10_BFS_03

输入: grid = ["@Aa"]
输出: -1

提示:

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

Solution

难以绷住,因为以前从没写过BFS的题,今天一上来就是一个Hard,且BFS存储的状态也很复杂。但是因为想全勤打卡一个月,所以还是硬着头皮写(实际上是不想看论文)。

当然全靠自己写是不可能的,从题解那里得到了一些精髓。

记录一下思考的过程。首先遍历一遍地图获取起点地址和钥匙数目。不考虑剪枝写了一个版本,使用队列,每次从队列开始的状态出发,上下左右移动到合法的下一步,当移动到拿到所有钥匙的时候停止。

这里需要使用一个数据结构存储坐标、移动步数和目前获得的钥匙数。很有意思,昨天的题目用比特来表示字符串,今天也可以用比特来表示钥匙。

在遍历的时候有三种情况。第一种是遍历到空地或者'@'(也就是起点),此时直接加入到队列就行。第二种是遍历到钥匙的时候,此时直接将当前的钥匙串一下钥匙即可更新钥匙串。第三种是遍历到锁的时候,此时将钥匙的对应位置一下锁,判断能不能通过。

代码实际上可以优化,这里对四个方向的遍历可以用循环实现,但是就先这样吧。。。不想改了。

class Solution {
private:
struct pos{
int a;
int b;
int d;
int key;
};
public:
int shortestPathAllKeys(vector<string>& grid) {
int n = grid.size();
int m = grid[0].size();
int key_n = 0;
int init_pr = -1, init_pc = -1;
for(int i=0;i<grid.size();i++){
for(int j=0;j<grid[0].size();j++){
if(isalpha(grid[i][j]))key_n++;
else if(grid[i][j]=='@'){
init_pr = i;
init_pc = j;
}
}
}
key_n /= 2;
// vector<vector<vector<int>>> min_d(n, vector<int>(m, vector<int>(31, -1)));
const int suc = (1<<key_n) - 1;
queue<pos> q;
struct pos st{init_pr, init_pc, 0, 0};
// min_d[init_pr][init_pc][0] = 0;
q.emplace(st);
while(!q.empty()){
auto f = q.front();
q.pop();
if(f.key==suc)return f.d;
if(f.d>n*m)break;
if(f.a-1>-1&&grid[f.a-1][f.b]!='#'){
struct pos f1 = f;
if(grid[f.a-1][f.b]=='.'||grid[f.a-1][f.b]=='@'){
f1.a -= 1;
f1.d += 1;
q.emplace(f1);
}
else if(grid[f.a-1][f.b]<='f'&&grid[f.a-1][f.b]>='a'){
f1.a -= 1;
f1.d += 1;
f1.key |= 1<<(grid[f.a-1][f.b]-'a');
q.emplace(f1);
}
else if(grid[f.a-1][f.b]<='F'&&grid[f.a-1][f.b]>='A'&&(f.key>>(grid[f.a-1][f.b]-'A'))&1){
f1.a -= 1;
f1.d += 1;
q.emplace(f1);
}
}
if(f.a+1<n&&grid[f.a+1][f.b]!='#'){
struct pos f1 = f;
if(grid[f.a+1][f.b]=='.'||grid[f.a+1][f.b]=='@'){
f1.a += 1;
f1.d += 1;
q.emplace(f1);
}
else if(grid[f.a+1][f.b]<='f'&&grid[f.a+1][f.b]>='a'){
f1.a += 1;
f1.d += 1;
f1.key |= 1<<(grid[f.a+1][f.b]-'a');
q.emplace(f1);
}
else if(grid[f.a+1][f.b]<='F'&&grid[f.a+1][f.b]>='A'&&(f.key>>(grid[f.a+1][f.b]-'A'))&1){
f1.a += 1;
f1.d += 1;
q.emplace(f1);
}
}
if(f.b-1>-1&&grid[f.a][f.b-1]!='#'){
struct pos f1 = f;
if(grid[f.a][f.b-1]=='.'||grid[f.a][f.b-1]=='@'){
f1.b -= 1;
f1.d += 1;
q.emplace(f1);
}
else if(grid[f.a][f.b-1]<='f'&&grid[f.a][f.b-1]>='a'){
f1.b -= 1;
f1.d += 1;
f1.key |= 1<<(grid[f.a][f.b-1]-'a');
q.emplace(f1);
}
else if(grid[f.a][f.b-1]<='F'&&grid[f.a][f.b-1]>='A'&&(f.key>>(grid[f.a][f.b-1]-'A'))&1){
f1.b -= 1;
f1.d += 1;
q.emplace(f1);
}
}
if(f.b+1<m&&grid[f.a][f.b+1]!='#'){
struct pos f1 = f;
if(grid[f.a][f.b+1]=='.'||grid[f.a][f.b+1]=='@'){
f1.b += 1;
f1.d += 1;
q.emplace(f1);
}
else if(grid[f.a][f.b+1]<='f'&&grid[f.a][f.b+1]>='a'){
f1.b += 1;
f1.d += 1;
f1.key |= 1<<(grid[f.a][f.b+1]-'a');
q.emplace(f1);
}
else if(grid[f.a][f.b+1]<='F'&&grid[f.a][f.b+1]>='A'&&(f.key>>(grid[f.a][f.b+1]-'A'))&1){
f1.b += 1;
f1.d += 1;
q.emplace(f1);
}
}
}
return -1;

}
};

这样子肯定是不能过的,所以需要考虑剪枝。用一个数组min_d[row][col][key]来存储状态。初始赋值为-1,之后,当遍历到一个状态时,就更新一下这个状态(根据队列FIFO的特性,如果该状态已被赋值,那么此时一定是最小的距离,之后遍历到这个状态的时候距离一定不会更小)。用哈希表存也可以。

当队列为空,表示无法达到。返回-1.

代码(C++)

class Solution {
private:
struct pos{
int a;
int b;
int d;
int key;
};
public:
int shortestPathAllKeys(vector<string>& grid) {
int n = grid.size();
int m = grid[0].size();
int key_n = 0;
int init_pr = -1, init_pc = -1;
for(int i=0;i<grid.size();i++){
for(int j=0;j<grid[0].size();j++){
if(isalpha(grid[i][j]))key_n++;
else if(grid[i][j]=='@'){
init_pr = i;
init_pc = j;
}
}
}
key_n /= 2;// 在上面的循环中,找到了起点位置,且获取了钥匙和锁的总数,除以2就是要收集的\U0001f511数目
vector<vector<vector<int>>> min_d(n, vector<vector<int>>(m, vector<int>(64, -1)));
const int suc = (1<<key_n) - 1;
queue<pos> q;
struct pos st{init_pr, init_pc, 0, 0};
min_d[init_pr][init_pc][0] = 0;
q.emplace(st);
while(!q.empty()){
auto f = q.front();
q.pop();
if(f.key==suc)return f.d;
if(f.a-1>-1&&grid[f.a-1][f.b]!='#'){
struct pos f1 = f;
if(grid[f.a-1][f.b]=='.'||grid[f.a-1][f.b]=='@'){
f1.a -= 1;
f1.d += 1;
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
else if(grid[f.a-1][f.b]<='f'&&grid[f.a-1][f.b]>='a'){
f1.a -= 1;
f1.d += 1;
f1.key |= 1<<(grid[f.a-1][f.b]-'a');
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
else if(grid[f.a-1][f.b]<='F'&&grid[f.a-1][f.b]>='A'&&(f.key>>(grid[f.a-1][f.b]-'A'))&1){
f1.a -= 1;
f1.d += 1;
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
}
if(f.a+1<n&&grid[f.a+1][f.b]!='#'){
struct pos f1 = f;
if(grid[f.a+1][f.b]=='.'||grid[f.a+1][f.b]=='@'){
f1.a += 1;
f1.d += 1;
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
else if(grid[f.a+1][f.b]<='f'&&grid[f.a+1][f.b]>='a'){
f1.a += 1;
f1.d += 1;
f1.key |= 1<<(grid[f.a+1][f.b]-'a');
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
else if(grid[f.a+1][f.b]<='F'&&grid[f.a+1][f.b]>='A'&&(f.key>>(grid[f.a+1][f.b]-'A'))&1){
f1.a += 1;
f1.d += 1;
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
}
if(f.b-1>-1&&grid[f.a][f.b-1]!='#'){
struct pos f1 = f;
if(grid[f.a][f.b-1]=='.'||grid[f.a][f.b-1]=='@'){
f1.b -= 1;
f1.d += 1;
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
else if(grid[f.a][f.b-1]<='f'&&grid[f.a][f.b-1]>='a'){
f1.b -= 1;
f1.d += 1;
f1.key |= 1<<(grid[f.a][f.b-1]-'a');
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
else if(grid[f.a][f.b-1]<='F'&&grid[f.a][f.b-1]>='A'&&(f.key>>(grid[f.a][f.b-1]-'A'))&1){
f1.b -= 1;
f1.d += 1;
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
}
if(f.b+1<m&&grid[f.a][f.b+1]!='#'){
struct pos f1 = f;
if(grid[f.a][f.b+1]=='.'||grid[f.a][f.b+1]=='@'){
f1.b += 1;
f1.d += 1;
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
else if(grid[f.a][f.b+1]<='f'&&grid[f.a][f.b+1]>='a'){
f1.b += 1;
f1.d += 1;
f1.key |= 1<<(grid[f.a][f.b+1]-'a');
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
else if(grid[f.a][f.b+1]<='F'&&grid[f.a][f.b+1]>='A'&&(f.key>>(grid[f.a][f.b+1]-'A'))&1){
f1.b += 1;
f1.d += 1;
if(min_d[f1.a][f1.b][f1.key]==-1){
q.emplace(f1);
min_d[f1.a][f1.b][f1.key] = f1.d;
}
}
}
}
return -1;

}
};

如果有人看到这,可以考虑帮我用一个循环把四个状态包起来。我自己懒得写了。





标签:f1,emplace,min,int,每日,11.10,grid,key,leetcode
From: https://blog.51cto.com/u_15763108/5841770

相关文章

  • leetcode744
    寻找比目标字母大的最小字母Category Difficulty Likes Dislikesalgorithms Easy(45.91%) 249 -TagsCompanies给你一个排序后的字符列表letters,列表中只包含小写英......
  • leetcode69
    x的平方根Category Difficulty Likes Dislikesalgorithms Easy(39.05%) 878 -TagsCompanies给你一个非负整数x,计算并返回x的算术平方根。由于返回类型是整数......
  • leetcode385
    两个数组间的距离值Category Difficulty Likes Dislikesalgorithms Easy(69.82%) 88 -TagsCompanies给你两个整数数组arr1,arr2和一个整数d,请你返回两个数组之......
  • 2022.11.10
    额,就是一个随笔而已,仿照\(\color{Red}{Cotsheep}\)那样,随便写点,权当水吧。AGC029C神妙的构造,报废的大脑。因为只用最后一位决定字典序,其他完全可以全用a填充。显......
  • leetcode852
    山脉数组的峰顶索引Category Difficulty Likes Dislikesalgorithms Easy(71.36%) 313 -TagsCompanies符合下列属性的数组arr称为山脉数组:arr.length>=3存在......
  • leetcode374
    猜数字大小Category Difficulty Likes Dislikesalgorithms Easy(51.88%) 265 -Tagsbinary-searchCompanies猜数字游戏的规则如下:每轮游戏,我都会从1到n随机选择......
  • leetcode704
    二分查找Category Difficulty Likes Dislikesalgorithms Easy(54.59%) 1037 -TagsCompanies给定一个n个元素有序的(升序)整型数组nums和一个目标值target,写一个......
  • 每日一题算法
    数字在升序数组中出现的次数描述给定一个长度为n的非降序数组和一个非负数整数k,要求统计k在数组中出现的次数解析排序数组的查找问题首先考虑二分法使用二分法......
  • 2022.11.10
    P5226小yue解密码反正我觉得这题挺毒瘤的(我太菜了),根本想不到第一篇题解的二分的思路(我太菜了)。但主要还是调代码调的我快逝世了,样例和Hack都过了但永远0pts。还好......
  • 每日一题-双指针
    判断子序列intj=0,i=0; while(i<mandj<n){if(b[i]==a[j]){j++;}i++;}cout<<(j==n?"Yes":"No");description......