【Leetcode】1778. Shortest Path in a Hidden Grid

题目地址:

https://leetcode.com/problems/shortest-path-in-a-hidden-grid/

给定一个未知的迷宫,提供一个类GridMaster的对象master,其能实现以下操作:
1、boolean canMove(char direction) 返回能否走这个方向,方向一共有四种,'U', 'D', 'L', 'R'
2、void move(char direction) 朝这个方向走一步,如果这个方向不能走(即遇到了障碍物),则停留在原地;
3、boolean isTarget() 返回当前是否在目标位置。
一开始的位置是起点位置,题目保证起点和目标都不是障碍物。问从起点到目标点的最短距离。每一步走的距离视为 1 1 1

可以认为该迷宫是个矩形网格,因为每一步只能走四个方向,由平移对称性,可以设初始点为 ( 0 , 0 ) (0,0) (0,0)。由于每走一步,master会真实地走到那个位置,所以一开始不方便用BFS。可以先用DFS把整个迷宫每个格子是空地还是障碍物给建出来,如果找到了目标点,则标记一下目标点的坐标。接着可以用A*算法求一下最短路。A*算法的相关概念可以参考https://blog.csdn.net/qq_46105170/article/details/114877098。这里的启发函数可以取成当前位置到目标点的曼哈顿距离,然后建最小堆,按 f ( x ) = g ( x ) + h ( x ) f(x)=g(x)+h(x) f(x)=g(x)+h(x)的从小到大的顺序出堆, g g g代表从起点到 x x x的真实走过的距离, h h h是启发函数。由A*算法的性质,当终点第一次出堆的时候,其距离是真实最短距离。代码如下:

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.PriorityQueue;

public class Solution {
    
    class Pair {
        int x, y, f;
        
        public Pair(int x, int y) {
            this.x = x;
            this.y = y;
        }
    
        @Override
        public boolean equals(Object o) {
            Pair pair = (Pair) o;
            return x == pair.x && y == pair.y;
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(x, y);
        }
    }
    
    private Pair target;
    
    public int findShortestPath(GridMaster master) {
        char[] d = {'U', 'R', 'D', 'L'};
        int[] dir = {-1, 0, 1, 0, -1};
        Pair start = new Pair(0, 0);
        Map<Pair, Integer> map = new HashMap<>();
        dfs(start, map, master, d, dir);
        return target == null ? -1 : bfs(start, map, dir);
    }
    
    private int bfs(Pair start, Map<Pair, Integer> map, int[] dir) {
        start.f = heur(start, target);
        PriorityQueue<Pair> minHeap = new PriorityQueue<>((p1, p2) -> Integer.compare(p1.f, p2.f));
        minHeap.offer(start);
        Map<Pair, Integer> dist = new HashMap<>();
        dist.put(start, 0);
        while (!minHeap.isEmpty()) {
            Pair cur = minHeap.poll();
            if (cur.equals(target)) {
                return dist.get(cur);
            }
    
            for (int i = 0; i < 4; i++) {
                Pair next = new Pair(cur.x + dir[i], cur.y + dir[i + 1]);
                if (map.get(next) != 0) {
                    if (!dist.containsKey(next) || dist.get(next) > dist.get(cur) + 1) {
                        dist.put(next, dist.get(cur) + 1);
                        next.f = dist.get(next) + heur(next, target);
                        minHeap.offer(next);
                    }
                }
            }
        }
        
        return -1;
    }
    
    private int heur(Pair cur, Pair target) {
        return Math.abs(cur.x - target.x) + Math.abs(cur.y - target.y);
    }
    
    // 从起点DFS,标记每个点是空地1,目标点2,还是障碍物0
    private void dfs(Pair cur, Map<Pair, Integer> map, GridMaster master, char[] d, int[] dir) {
        if (master.isTarget()) {
            map.put(cur, 2);
            target = cur;
        } else {
            map.put(cur, 1);
        }
        
        int x = cur.x, y = cur.y;
        for (int i = 0; i < 4; i++) {
            int nextX = x + dir[i], nextY = y + dir[i + 1];
            Pair next = new Pair(nextX, nextY);
            if (map.containsKey(next)) {
                continue;
            }
            
            if (master.canMove(d[i])) {
                master.move(d[i]);
                dfs(next, map, master, d, dir);
                // 回溯回来的时候回退一格
                master.move(d[(i + 2) % 4]);
            } else {
                map.put(next, 0);
            }
        }
    }
}

时空复杂度 O ( m n ) O(mn) O(mn) m n mn mn是迷宫的行数和列数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值