📅  最后修改于: 2023-12-03 15:41:09.999000             🧑  作者: Mango
这道题目中,我们要求得须藤放置游戏中,从左上角出发到右下角的最短路径长度。
须藤放置是一个单人益智类游戏,玩家通过连接数字和对应方向的箭头,完成游戏目标。
游戏界面中,有一个 $n\times n$ 的矩形,每个格子中都填有数字和箭头,其中数字表示通过此格的步数,箭头表示通过此格的方向,箭头的类型可能为上下左右和转角四种类型。
现在,请你编写一个算法,计算从左上角出发到右下角的最短路径长度。
其中,你可以假设每个格子尺寸相等,步数为整数。
样例:
输入:
[
[0, 2, -1, 1],
[3, -1, 2, 1],
[-1, -1, 0, 1],
[4, 5, 6, 0],
[2, 3, -1, 7]
]
输出:
12
这道题目是一道经典的图论问题,需要使用最短路径算法进行求解。
在这个问题中,我们可以使用广度优先搜索 (BFS) 或 Dijkstra 算法进行求解最短路径。
由于该题中,每个格子中填有数字和箭头,并且各个格子之间通过箭头可以连接,因此可以将问题转化为有向图问题。
对于有向图问题,广度优先搜索常被用来求解最短路径,因为扩展新的状态所消耗的时间是相等的,因此当遇到目标节点时,广度优先搜索一定能够得到最优解。
而对于无权图的单源最短路径问题,BFS 是最优解,而在存在负权边时,Dijkstra 算法是更好的选择。
由于这道题输入中可能存在负权边,因此建议使用 Dijkstra 算法来求解最短路径。
通过邻接表来存储有向图。
通过优先队列和数组来实现 Dijkstra 算法。
从起点出发,遍历每一个能够到达的点,并更新到达该点的最短路径。
将所有已经遍历过的点加入集合中。
通过遍历到的点,再计算能够到达其他点的最短路径,并更新到数组和队列中。
循环以上步骤,直到遍历到终点。
CODE:
public class Main {
/**
* 使用 Dijkstra 算法求解最短路径
* @param grid 二维数组存储网格矩阵
* @return 从左上角出发到右下角的最短路径长度
*/
public int shortestPath(int[][] grid) {
int ROW = grid.length, COL = grid[0].length;
// 邻接表存储有向图
List<int[]>[] graph = new ArrayList[ROW * COL];
for (int i = 0; i < ROW * COL; i++) {
graph[i] = new ArrayList<>();
}
// 将二维数组转化为邻接表
int[] dx = {-1, 0, 1, 0}, dy = {0, -1, 0, 1};
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (grid[i][j] == -1) {
continue;
}
int idx = i * COL + j;
for (int k = 0; k < 4; k++) {
int ni = i + dx[k], nj = j + dy[k];
if (ni >= 0 && ni < ROW && nj >= 0 && nj < COL && grid[ni][nj] != -1) {
int nidx = ni * COL + nj;
int weight = grid[i][j];
if (k != grid[i][j + 2]) {
weight += 1;
}
graph[idx].add(new int[]{nidx, weight});
}
}
}
}
// 从起点开始遍历
int start = 0, end = ROW * COL - 1;
int[] distance = new int[ROW * COL];
Arrays.fill(distance, Integer.MAX_VALUE);
distance[start] = 0;
// 优先队列
PriorityQueue<int[]> queue = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
queue.offer(new int[]{start, 0});
Set<Integer> visited = new HashSet<>();
while (!queue.isEmpty()) {
int[] curr = queue.poll();
int idx = curr[0], dist = curr[1];
if (visited.contains(idx)) {
continue;
}
visited.add(idx);
if (idx == end) {
break;
}
// 更新能够到达的点的最短距离
for (int[] neighbor : graph[idx]) {
int nidx = neighbor[0], weight = neighbor[1];
if (!visited.contains(nidx) && dist + weight < distance[nidx]) {
distance[nidx] = dist + weight;
queue.offer(new int[]{nidx, distance[nidx]});
}
}
}
if (distance[end] == Integer.MAX_VALUE) {
return -1;
}
return distance[end];
}
}