一、Floyd 算法
题目连接:97. 小明逛公园 (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——Floyd 算法Carl说:Floyd 算法代码很简单,但真正理解起原理 还是需要花点功夫,大家在看代码的时候,会发现 Floyd 的代码很简单,甚至看一眼就背下来了,但我为了讲清楚原理,本篇还是花了大篇幅来讲解。
思路
本题为多源最短路径问题,即 求多个起点到多个终点的多条最短路径。
Floyd算法
应用范围:对边的权重没有要求,正负均可。
核心思想:动态规划。
动归五部曲:
1. 确定 dp 数组及下标含义:grid[ i ][ j ][ k ] = m,表示节点 i 到 节点 j 以[1...k] 集合为中间节点的最短距离为 m 。
2. 确定递推公式:
case1. 节点 i 到 节点 j 的最短路径经过节点 k。grid[i][j][k] = grid[i][k][k - 1] + grid[k][j][k - 1],经过 k ,两段路都不包含节点 k ,所以中间节点集合为[1, k - 1]
case2. 节点 i 到 节点 k 的最短路径不经过节点 k 。grid[i][j][k] = grid[i][j][k - 1],不经过节点 k ,中间节点集合为 [1, k - 1]
求最短路径,两种情况取最小值,grid[i][j][k] = min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1])
3. 确定dp数组如何初始化:
for _ in range(m):
p1, p2, val = map(int, input().split())
# 注意这里是双向图
grid[p1][p2][0] = val
grid[p2][p1][0] = val
4. 确定遍历顺序:把 i 和 j 看作三维左边的平层,k 看作垂直向上的,遍历时是从底向上一层一层遍历,所以 k 是最外层循环,i 和 j 的先后顺序无所谓。
5. 举例推导dp数组。
def floyd(n, edges):
# 初始化三维数组
inf = 10005
grid = [[[inf] * (n + 1) for _ in range(n + 1)]for _ in range(n + 1)]
# 读取边的信息,初始 dp 数组
for p1, p2, val in edges:
# 注意,为双向图
grid[p1][p2][0] = val
grid[p2][p1][0] = val
for k in range(1, n + 1):
for i in range(1, n + 1):
for j in range(1, n + 1):
grid[i][j][k] = min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1])
return grid
if __name__ == '__main__':
n, m = map(int, input().split())
edges = []
for _ in range(m):
p1, p2, val = map(int, input().split())
edges.append((p1, p2, val))
grid = floyd(n, edges)
# q 个观景计划
q = int(input())
for _ in range(q):
# 读取每个计划的起点和终点
start, end = map(int, input().split())
if grid[start][end][n] == 10005:
print(-1)
else:
print(grid[start][end][n])
二、A star算法
题目连接:127. 骑士的攻击 (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——A star算法Carl说:一般 笔试或者 面试的时候,不会考察A*, 都是会结合具体业务场景问 A*算法,例如:地图导航,游戏开发 等等。
import heapq
import sys
# 定义方向
dir = [(-2, -1), (-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2)]
# A* 格子
moves = [[0] * 1001 for _ in range(1001)]
# 计算启发式函数:欧氏距离的平方
def heuristic(x, y, target_x, target_y):
return (x - target_x) ** 2 + (y - target_y) ** 2
# A* 算法
def a_star(start_x, start_y, target_x, target_y):
# 将 start 定义为 (g, h, f, x, y)
open_list = []
heapq.heappush(open_list, (0, 0, heuristic(start_x, start_y, target_x, target_y), start_x, start_y))
while open_list:
g, _, h, x, y = heapq.heappop(open_list)
# 达到目标
if x == target_x and y == target_y:
return moves[x][y]
for dx, dy in dir:
next_x = x + dx
next_y = y + dy
# 检查边界
if next_x < 1 or next_x > 1000 or next_y < 1 or next_y > 1000:
continue
# 如果没访问过
if moves[next_x][next_y] == 0:
# 更新步数
moves[next_x][next_y] = g + 1
# 计算 F 值
next_g = g + 5 # 每次移动消耗 5
next_h = heuristic(next_x, next_y, target_x, target_y)
heapq.heappush(open_list, (next_g, next_h, next_h, next_x, next_y))
return -1
if __name__ == '__main__':
n = int(input())
for _ in range(n):
a1, a2, b1, b2 = map(int, input().split())
# 初始化 moves 数组
for i in range(1001):
for j in range(1001):
moves[i][j] = -1
# 调用 A* 算法
result = a_star(a1, a2, b1, b2)
print(result)
三、最短路算法总结篇
文章连接:代码随想录 (programmercarl.com)——最短路算法总结篇
单源且边为正数,用 dijkstra,一般情况下可以直接用 堆优化版本。
单源且边可为负数,用 Bellman-Ford,一般情况下可以直接用 spfa。
有负权回路,优先 Bellman-Ford。
多源点求最短路径,用 floyd。