1.Floyd 算法精讲
题目链接:Floyd 算法精讲
文档讲解: 代码随想录
本题是多源最短路,即求多个起点到多个终点的多条最短路径。Floyd算法对边的权值正负没有要求,都可以处理,其核心思想是动态规划。
动规五部曲:
(1)确定dp数组和下标
用grid存图,grid[i][j][k]表示节点 i 到节点 j 以[1,…,k] 集合为中间节点的最短距离
(2)递推关系式
分两种情况,一种是节点 i 到节点 j 的最短路径经过节点 k,grid[i][j][k] = grid[i][k][k-1] + grid[k][j][k-1];另一种情况,节点 i 到节点 j 的最短路径不经过节点 k,grid[i][j][k] = grip[i][j][k-1];两者取最小值
(3)初始化
刚开始初始化 k 是不确定的,如果赋值为1,那么不能知道节点 i 到节点 j 经过1个节点的最短距离,所以只能把 k 赋值为0,本题中节点0是无意义的,节点是从1到n。本题求的是最小值,所以其余值初始化为最大值
(4)遍历顺序
好比三维坐标,i,j是平层,而k是垂直向上的,遍历顺序是从底向上一层一层去遍历,所以遍历时 k 的for循环一定在最外面,这样才能一层一层去遍历,而 i 和 j 的先后顺序无所谓
(5)打印数组
#基于三维数组
n,m = map(int,input().split())
#创建三维数组
grid = [[[float('inf')] * (n+1) for _ in range(n+1)] for _ in range(n+1)]
#初始化
for _ in range(m):
u,v,w = map(int,input().split())
grid[u][v][0] = w
grid[v][u][0] = w
#floyd
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][j][k-1],grid[i][k][k-1] + grid[k][j][k-1])
#输出结果
q = int(input())
for _ in range(q):
start,end = map(int,input().split())
if grid[start][end][n] == float('inf'):
print(-1)
else:
print(grid[start][end][n])
#基于二维数组
n,m = map(int,input().split())
grid = [[float('inf')] * (n+1) for _ in range(n+1)]
#初始化
for _ in range(m):
u,v,w = map(int,input().split())
grid[u][v] = w
grid[v][u] = w
for k in range(1,n+1):
for i in range(1,n+1):
for j in range(1,n+1):
grid[i][j] = min(grid[i][j],grid[i][k] + grid[k][j])
#输出结果
q = int(input())
for _ in range(q):
start,end = map(int,input().split())
if grid[start][end] == float('inf'):
print(-1)
else:
print(grid[start][end])
2.A * 算法精讲 (A star算法)
题目链接:A * 算法精讲 (A star算法)
文档讲解: 代码随想录
bfs是没有目的性的一圈一圈搜索,而A*算法是有方向性的去搜索,通过启发式函数指引搜索方向。本题的图是无权网络状,使用欧氏距离来计算权值,对队列里的节点进行排序。
import heapq
direction = [[-2,1],[-1,2],[1,2],[2,1],[2,-1],[1,-2],[-1,-2],[-2,-1]]
maxx = 1001
#定义骑士类
class Knight:
def __init__(self,x,y,g,h):
self.x = x
self.y = y
self.g = g #从起点到该点的距离
self.h = h #从该点到终点的距离
self.f = g + h
def __lt__(self,other):
return self.f < other.f #小顶堆,按照f排序,统一不开根号可以提高精度
#计算欧式距离
def heuristic(k,b1,b2):
return (k.x - b1) ** 2 + (k.y - b2) ** 2
def astar(start_x,start_y,end_x,end_y):
#记录路径长度
move = [[0] * maxx for _ in range(maxx)]
que = []
start = Knight(start_x,start_y,0,heuristic(Knight(start_x,start_y,0,0),end_x,end_y))
heapq.heappush(que,start)
while que:
cur = heapq.heappop(que)
#终止条件,达到终点
if cur.x == end_x and cur.y == end_y:
return move[cur.x][cur.y]
for dx,dy in direction:
next_x = cur.x + dx
next_y = cur.y + dy
if 1 <= next_x < maxx and 1 <= next_y < maxx and move[next_x][next_y] == 0:
move[next_x][next_y] = move[cur.x][cur.y] + 1
#走马日,1**2+2**2=5
next_k = Knight(next_x,next_y,cur.g + 5,heuristic(Knight(next_x,next_y,0,0),end_x,end_y))
heapq.heappush(que,next_k)
return -1
n = int(input())
for _ in range(n):
a1,a2,b1,b2 = map(int,input().split())
res = astar(a1,a2,b1,b2)
print(res)
A算法搜的路径如何,完全取决于启发式函数怎么写,不能保证一定是最短路,设计启发式函数时要考虑时间效率和准确度之间的权衡。该算法的缺陷:A只从队列里取出距离终点最近的节点,大量不需要访问的节点都在队列里,会造成空间的过度消耗;有种常见是解决不了的,给出多个可能的目标,然后在多个可能目标中选择最近的节点,A*算法只擅长给出明确的目标然后找到最短路径。