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 == 21 <= specialRoads.length <= 200specialRoads[i].length == 5startX <= x1i, x2i <= targetXstartY <= y1i, y2i <= targetY
思路分析
- 左边xy处理,x 左移 32 位 后加上 y,也就是用一个 long 装两个 int,因为两个 1e5 int 范围装不下,左移 32 位 更好算,左移 30 位也是可以的,以下压缩坐标用 flatXY 表示
- 哈希记录映射 flatXY->最短距离,哈希 visited 记录已经访问的点,列表记录当前需要检查的坐标范围
- 每次取距离最短的下标(它无法再压缩了,剩下任意一个点到这个点的距离一定比当前距离大,但是通过这个短距离再到达其他点,可能获得其他点的更短距离),这个点因为无法再次缩短,标为已访问
- 如果刚好当前点为已访问点,则结束
- 当前点可以有两种选择:
- 直接去终点,到达终点,花费:到达当前点的代价+去终点 x 方向代价+去终点 y 方向代价
- 走特殊路径,到达特殊路径终点,花费:到达当前点代价+去特殊路径起点 x 方向代价+去特殊路径起点 y 方向代价+特殊路径起点到终点代价,如果是更优的走法,则加到列表中接着走
- 当剩余节点中,最短距离为终点时,结束
算法代码
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)
};
}
结果详情
算法复杂度
- 空间复杂度:
- 时间复杂度:
在掘金(JUEJIN)一起进步,一起成长!