算法初探LeetCode-前往目标的最小代价

115 阅读3分钟

LeetCode2662-前往目标的最小代价

给你一个数组 start ,其中 start = [startX, startY] 表示你的初始位置位于二维空间上的 (startX, startY) 。另给你一个数组 target ,其中 target = [targetX, targetY] 表示你的目标位置 (targetX, targetY) 。

从位置 (x1, y1) 到空间中任一其他位置 (x2, y2) 的代价是 |x2 - x1| + |y2 - y1| 。

给你一个二维数组 specialRoads ,表示空间中存在的一些特殊路径。其中 specialRoads[i] = [x1i, y1i, x2i, y2i, costi] 表示第 i 条特殊路径可以从 (x1i, y1i) 到 (x2i, y2i) ,但成本等于 costi 。你可以使用每条特殊路径任意次数。

返回从 (startX, startY) 到 (targetX, targetY) 所需的最小代价。

示例 1:

输入: start = [1,1], target = [4,5], specialRoads = [[1,2,3,3,2],[3,4,4,5,1]]
输出: 5
解释: 从 (1,1) 到 (4,5) 的最优路径如下:
- (1,1) -> (1,2) ,移动的代价是 |1 - 1| + |2 - 1| = 1 。
- (1,2) -> (3,3) ,移动使用第一条特殊路径,代价是 2 。
- (3,3) -> (3,4) ,移动的代价是 |3 - 3| + |4 - 3| = 1.
- (3,4) -> (4,5) ,移动使用第二条特殊路径,代价是 1 。
总代价是 1 + 2 + 1 + 1 = 5 。
可以证明无法以小于 5 的代价完成从 (1,1) 到 (4,5) 。

示例 2:

输入: start = [3,2], target = [5,7], specialRoads = [[3,2,3,4,4],[3,3,5,5,5],[3,4,5,6,6]]
输出: 7
解释: 最优路径是不使用任何特殊路径,直接以 |5 - 3| + |7 - 2| = 7 的代价从初始位置到达目标位置。

提示:

  • start.length == target.length == 2
  • 1<=startX<=targetX<=1051 <= startX <= targetX <= 10^5
  • 1<=startY<=targetY<=1051 <= startY <= targetY <= 10^5
  • 1 <= specialRoads.length <= 200
  • specialRoads[i].length == 5
  • startX <= x1i, x2i <= targetX
  • startY <= y1i, y2i <= targetY
  • 1<=costi<=1051 <= costi <= 10^5

思路分析

  1. 左边xy处理,x 左移 32 位 后加上 y,也就是用一个 long 装两个 int,因为两个 1e5 int 范围装不下,左移 32 位 更好算,左移 30 位也是可以的,以下压缩坐标用 flatXY 表示
  2. 哈希记录映射 flatXY->最短距离,哈希 visited 记录已经访问的点,列表记录当前需要检查的坐标范围
  3. 每次取距离最短的下标(它无法再压缩了,剩下任意一个点到这个点的距离一定比当前距离大,但是通过这个短距离再到达其他点,可能获得其他点的更短距离),这个点因为无法再次缩短,标为已访问
  4. 如果刚好当前点为已访问点,则结束
  5. 当前点可以有两种选择:
  • 直接去终点,到达终点,花费:到达当前点的代价+去终点 x 方向代价+去终点 y 方向代价
  • 走特殊路径,到达特殊路径终点,花费:到达当前点代价+去特殊路径起点 x 方向代价+去特殊路径起点 y 方向代价+特殊路径起点到终点代价,如果是更优的走法,则加到列表中接着走
  1. 当剩余节点中,最短距离为终点时,结束

算法代码

public int minimumCost(int[] start, int[] target, int[][] specialRoads) {
    List < int[] > list = new ArrayList < > ();
    Map < Long, Integer > map = new HashMap < > ();
    map.put(flat(start[0], start[1]), 0);
    list.add(new int[] {
        start[0], start[1]
    });
    list.add(new int[] {
        target[0], target[1]
    });
    Set < Long > visited = new HashSet < > ();
    long ft = flat(target[0], target[1]);
    while (true) {
        long fv = ft;
        for (int[] item: list) {
            long flatItem = flat(item[0], item[1]);
            int a = map.getOrDefault(flatItem, Integer.MAX_VALUE);
            int b = map.getOrDefault(fv, Integer.MAX_VALUE);

            if (!visited.contains(flatItem) && a < b) {
                fv = flatItem;
            }
        }
        if (fv == ft) return map.get(fv);
        visited.add(fv);
        int[] cp = collapse(fv);
        map.merge(ft, map.get(fv) + target[0] - cp[0] + target[1] - cp[1], Integer::min);
        for (int[] nx: specialRoads) {
            long flatEnd = flat(nx[2], nx[3]);
            if (Math.abs(cp[0] - nx[0]) + Math.abs(cp[1] - nx[1]) + nx[4] + map.get(fv) < map.getOrDefault(flatEnd, Integer.MAX_VALUE)) {
                map.put(flatEnd, Math.abs(cp[0] - nx[0]) + Math.abs(cp[1] - nx[1]) + nx[4] + map.get(fv));
                list.add(new int[] {
                    nx[2], nx[3]
                });
            }
        }
    }
}

private long flat(int a, int b) {
    return ((long) a << 32) + b;
}

private int[] collapse(long v) {
    return new int[] {
        (int)(v >> 32), (int)(v & Integer.MAX_VALUE)
    };
}

结果详情

Snipaste_2023-05-30_23-16-28.png

算法复杂度

  • 空间复杂度:O(1)O(1)
  • 时间复杂度:O(n)O(n)

掘金(JUEJIN)一起进步,一起成长!