97.小明逛公園
https://kamacoder.com/problempage.php?pid=1155
Floyd 演算法精講
https://www.programmercarl.com/kamacoder/0097.小明逛公園.html#floyd-演算法精講
Floyd 演算法精講
-
問題總結:雙向道路;路徑規劃;多個起點到多個終點
-
核心思想:動態規劃
- 確定dp陣列和下標含義:grid[i][j][k] = m 含義:節點i到節點j以【1...k】集合為中間節點的最短距離為m k是集合
- 確定遞推公式:
- 節點i->節點j最短路徑經過節點k:
grid[i][j][k] = grid[i][k][k-1]+grid[k][j][k-1] - 節點i->節點j最短路徑不經過節點k
grid[i][j][k] = grid[i][j][k-1] - 最終遞推式為:
grid[i][j][k] = min(grid[i][k][k-1]+grid[k][j][k-1],grid[i][j][k-1])
- 節點i->節點j最短路徑經過節點k:
- dp陣列初始化
grid[i][j][0]:節點0無意義;從節點0開始初始化;
- 確定遍歷順序:從底層一層一層遍歷上去;
- 推導dp陣列
點選檢視程式碼
def floyd(n,grid): 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 def main(): max_int = 10005 n,m = map(int,input().split()) grid = [[[max_int]*(n+1) for _ in range(n+1)] for _ in range(n+1)] for _ in range(m): p1,p2,w = map(int,input().split()) grid[p1][p2][0] = w grid[p2][p1][0] = w grid = floyd(n,grid) q = int(input()) for _ in range(q): start,end = map(int,input().split()) if grid[start][end][n]==max_int: print(-1) else: print(grid[start][end][n]) if __name__ == '__main__': main()
Astar演算法
-
其實是BFS的變形;
-
BFS和Astar演算法的動畫如圖:https://kamacoder.com/tools/knight.html
-
Astar遍歷:
-
BFS遍歷:
-
BFS 是沒有目的性的 一圈一圈去搜尋, 而 A * 是有方向性的去搜尋。
-
其關鍵在於 啟發式函式。
-
每個節點的權值為F,給出公式為:F = G + H
- G:起點達到目前遍歷節點的距離
- F:目前遍歷的節點到達終點的距離
-
本題的圖是無權網格狀,在計算兩點距離通常有如下三種計算方式:
- 曼哈頓距離,計算方式: d = abs(x1-x2)+abs(y1-y2)
- 歐氏距離(尤拉距離) ,計算方式:d = sqrt( (x1-x2)^2 + (y1-y2)^2
- 切比雪夫距離,計算方式:d = max(abs(x1 - x2), abs(y1 - y2))
-
計算出來 F 之後,按照 F 的 大小,來選去出佇列的節點。可以使用 優先順序佇列 幫我們排好序,每次出佇列,就是F最小的節點。
-
多個目標找最近目標(特別是潛在目標數量很多的時候),可以考慮 Dijkstra ,BFS 或者 Floyd
點選檢視程式碼
import heapq import math def heuristic(x1, y1, x2, y2): # 歐式距離作為啟發式函式 return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) def a_star(a1,a2,b1,b2): # 定義騎士的8個移動方向 directions = [(-2, -1), (-1, -2), (1, -2), (2, -1), (2, 1), (1, 2), (-1, 2), (-2, 1)] pq = [] heapq.heappush(pq,(0,0,a1,a2)) visited = set() while pq: total_cost,curr_cost,x,y = heapq.heappop(pq) if (x,y) == (b1,b2): return curr_cost if (x,y) in visited: continue visited.add((x,y)) for dx,dy in directions: nx,ny = x+dx,y+dy if 1<=nx<=1000 and 1<=ny<=1000 and (nx,ny) not in visited: new_cost = curr_cost+1 total_cost = new_cost+heuristic(nx,ny,b1,b2) heapq.heappush(pq,(total_cost,new_cost,nx,ny)) return -1 def main(): # 定義騎士的8個移動方向 directions = [(-2, -1), (-1, -2), (1, -2), (2, -1), (2, 1), (1, 2), (-1, 2), (-2, 1)] n = int(input()) res = [] for _ in range(n): a1,a2,b1,b2 = map(int,input().split()) res_i = a_star(a1,a2,b1,b2) res.append(res_i) for res_i in res: print(res_i) if __name__ == '__main__': main()
路徑規劃演算法總結
演算法 | 源點數 | 邊的權值是否為負數 | 檢測負權迴路 | 有限節點最短路徑 | 思路 |
---|---|---|---|---|---|
dijkstra樸素版 | 單源 | 不可以 | 不可以 | 不可以 | 遍歷未被訪問的節點更新mindist |
dijkstra堆最佳化版 | 單源 | 不可以 | 不可以 | 不可以 | 用堆疊最佳化 |
Bellman_ford | 單源 | 可以 | 可以 | 可以 | 遍歷邊更新mindist |
SPFA | 單源 | 可以 | 可以 | 可以 | 只最佳化當前節點相連的點 |
Floyd | 多源 | 可以 | 可以 | 不可以 | 三維動態最佳化 |
圖演算法總結
- 深搜與廣搜:遍歷;
- 並查集:判斷是否相連;
- 最小生成樹;
- 拓撲排序;
- 最短路徑演算法;