【每日一题DAY23】LC864获取所有钥匙的最短路径 |状态压缩 BFS

简介: n件物品,每个物品有2个状态(状态1和状态2),那么可以用n位2进制表示物品的状态

状态压缩


  • 状态压缩:一个维度能表示所有物品的状态情况


  • n件物品,每个物品有2个状态(状态1和状态2),那么可以用n位2进制表示物品的状态

。第k位为1时,表示第k件物品的状态为状态1

。第k位为0时,表示第k件物品的状态为状态2


  • 比如

。LC864中可用一个int类型二进制代表当前收集到钥匙的情况

。背包问题中每件物品放或不放的情况


  • 状态检测:(state >> k) & 1

。返回1说明第k位为1

。返回0说明第k位为0


  • 状态更新:state |= 1 << k

。将 state 的第 k位设置为 1


获取所有钥匙的最短路径【LC864】


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


  • ‘.’ 代表一个空房间
  • ‘#’ 代表一堵
  • ‘@’ 是起点
  • 小写字母代表钥匙
  • 大写字母代表锁


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


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


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


You are given an m x n grid grid where:


  • '.' is an empty cell.
  • '#' is a wall.
  • '@' is the starting point.
  • Lowercase letters represent keys.
  • Uppercase letters represent locks.


You start at the starting point and one move consists of walking one space in one of the four cardinal directions. You cannot walk outside the grid, or walk into a wall.


If you walk over a key, you can pick it up and you cannot walk over a lock unless you have its corresponding key.


For some 1 <= k <= 6, there is exactly one lowercase and one uppercase letter of the first k letters of the English alphabet in the grid. This means that there is exactly one key for each lock, and one lock for each key; and also that the letters used to represent the keys and locks were chosen in the same order as the English alphabet.


Return the lowest number of moves to acquire all keys. If it is impossible, return -1.


写了快两个小时的回溯没写出来,最后看的三叶姐的题解。


感觉可以看做四叉树,首先找到树的根节点(起点@),然后压入队列,每个节点有四种方向,判断位置是否合法,然后根据元素进行相应的操作。每个位置可能重复到达,因此需要记录每个位置对应的步数。第一次找到所有钥匙时,返回前一个位置的步数+1


  • 思路:


。使用状态压缩记录当前收集到钥匙的情况state,便于使用位运算进行钥匙检测和更新钥匙收集状态


。起始遍历一次棋盘


  • 找到起点位置,并将其进行入队,队列维护的是 (x,y,state)三元组状态(其中 (x,y)代表当前所在的棋盘位置,state 代表当前的钥匙收集情况),


  • 同时统计整个棋盘所包含的钥匙数量 cnt


  • 并使用 数组/哈希表 记录到达每个位置所需要消耗的最小步数 step


。然后进行四联通方向的BFS,从一个位置搜寻它的四个方向,如果能够到达某一个位置,并且步数比先前到达该位置还要小,那么将其的三元组加入队列的末端,再次搜寻【BFS可以保证第一次找到全部钥匙时一定是最小步数】


。当 BFS 过程中遇到 state = (1 << cnt) - 1 时,代表所有钥匙均被收集完成,可结束搜索


  • 状态压缩:使用一个int类型二进制state代表当前收集到钥匙的情况


。第k位为1时,表示第k个钥匙已被收集

。第k位为0时,表示第k个钥匙未被收集


  • 状态检测:(state >> k) & 1


。返回1说明第k位为1

。返回0说明第k位为0


  • 状态更新:state |= 1 << k


。将 state 的第 k位设置为 1,代表当前新收集到种类编号为 k 的钥匙


  • 四联通方向BFS的搜索过程


。边界外:跳过

。遇到锁时


  • 没有对应钥匙:跳过
  • 有钥匙时
  • 步数小于当前位置的最小步数:更新步数,入队
  • 步数大于当前位置的最小步数:跳过


。遇到钥匙时,需要更新对应的 state

  • 步数小于当前位置的最小步数:更新步数,入队
  • 步数大于当前位置的最小步数:跳过


  • 代码


class Solution {
    static int N = 35, K = 10, INF = 0x3f3f3f3f;
    static int[][][] dist = new int[N][N][1 << K];
    static int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
    public int shortestPathAllKeys(String[] g) {
        int n = g.length, m = g[0].length(), cnt = 0;
        Deque<int[]> d = new ArrayDeque<>();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                Arrays.fill(dist[i][j], INF);
                char c = g[i].charAt(j);
                if (c == '@') {
                    d.addLast(new int[]{i, j, 0});
                    dist[i][j][0] = 0;
                } else if (c >= 'a' && c <= 'z') cnt++;
            }
        }
        while (!d.isEmpty()) {
            int[] info = d.pollFirst();
            int x = info[0], y = info[1], cur = info[2], step = dist[x][y][cur];
            for (int[] di : dirs) {
                int nx = x + di[0], ny = y + di[1];
                if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
                char c = g[nx].charAt(ny);
                if (c == '#') continue;
                if ((c >= 'A' && c <= 'Z') && (cur >> (c - 'A') & 1) == 0) continue;
                int ncur = cur;
                if (c >= 'a' && c <= 'z') ncur |= 1 << (c - 'a');
                if (ncur == (1 << cnt) - 1) return step + 1;
                if (step + 1 >= dist[nx][ny][ncur]) continue;
                dist[nx][ny][ncur] = step + 1;
                d.addLast(new int[]{nx, ny, ncur});
            }
        }
        return -1;
    }
}
作者:宫水三叶
链接:https://leetcode.cn/problems/shortest-path-to-get-all-keys/solutions/1960544/by-ac_oier-5gxc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


  • 复杂度


。时间复杂度:O ( n ∗ m ∗ 2 k )

。空间复杂度:O ( n ∗ m ∗ 2 k )

目录
相关文章
|
15天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
|
7天前
|
云安全 人工智能 安全
Dify平台集成阿里云AI安全护栏,构建AI Runtime安全防线
阿里云 AI 安全护栏加入Dify平台,打造可信赖的 AI
|
10天前
|
人工智能 运维 Java
Spring AI Alibaba Admin 开源!以数据为中心的 Agent 开发平台
Spring AI Alibaba Admin 正式发布!一站式实现 Prompt 管理、动态热更新、评测集构建、自动化评估与全链路可观测,助力企业高效构建可信赖的 AI Agent 应用。开源共建,现已上线!
931 29
|
9天前
|
机器学习/深度学习 人工智能 搜索推荐
万字长文深度解析最新Deep Research技术:前沿架构、核心技术与未来展望
近期发生了什么自 2025 年 2 月 OpenAI 正式发布Deep Research以来,深度研究/深度搜索(Deep Research / Deep Search)正在成为信息检索与知识工作的全新范式:系统以多步推理驱动大规模联网检索、跨源证据。
672 52
|
3天前
|
监控 BI 数据库
打工人救星!来看看这两家企业如何用Quick BI让业务更高效
Quick BI专业版监控告警助力企业高效运作,通过灵活配置规则与多渠道推送,让数据异常早发现、快响应,推动业务敏捷决策与持续增长。
打工人救星!来看看这两家企业如何用Quick BI让业务更高效
|
7天前
|
文字识别 测试技术 开发者
Qwen3-VL新成员 2B、32B来啦!更适合开发者体质
Qwen3-VL家族重磅推出2B与32B双版本,轻量高效与超强推理兼备,一模型通吃多模态与纯文本任务!
594 11