DP
  這道題目還可以用動態規劃解決。在圖論中解決最短路徑問題有Dijkstra算法和bellman-ford算法。這道題目也需要用到DP。所以先學習一下這兩個算法的思想和區別。
  兩個算法比較
  Dijstra算法用來解決單源最短路徑問題。具體內容看[文章]
 
算法解決問題適用范圍解決思路松弛對象
| Dijkstra算法 | 單源最短路徑 | 權重是正的圖 | 貪心 | 頂點 | 
| bellman-ford算法 | 單源最短路徑 | 圖,不可以有負權環路 | 動態規劃 | 邊 | 
ps:雖然我也不明白為什么叫松弛。只知道是不斷處理,不斷優化的對象。
  之所以做比較是想明白這兩個算法有什么區別。以及第二個算法的思想是怎么應用在本題目。
  本題目的DP方案參考鏈接。
  首先使用Floy算法計算任意兩點之間的最短路徑。接著使用遞歸方程dp[i][j] = Math.min(dp[i][j],dp[包含u但是不包含v 的狀態][u]+dist[u][v])。
 
public class ShortestPathVisitingAllNodesDP {private int[][] dis = new int[15][15];private int[][] dp =new int[1<<13][13];public int shortestPathLength(int[][] graph) {int N = graph.length;for (int[] row: dis) Arrays.fill(row, N*N);for (int i=0; i<N; i++) {for (int j=0; j<graph[i].length; j++) {int u = i, v = graph[i][j];dis[u][v] = 1;}}floyd(N);return dp(N);}/*** Floy算法:任意兩點之間的最短距離* @param n*/public void floyd(int n) {for(int k=0; k<n; k++)for(int i=0; i<n; i++)for(int j=0; j<n; j++)dis[i][j] = Math.min(dis[i][j], dis[i][k]+dis[k][j]);}private int dp(int n) {for (int[] row: dp) Arrays.fill(row, n*n);for (int i=0; i<n; i++)dp[1<<i][i] = 0;for (int group=1; group<(1<<n); group++)for (int u=0; u<n; u++)for (int v=0; v<n; v++) {int src = 1 << u, dst = 1 << v;//group包含src,但是不包含dstif ((group & src)!=0 && (group & dst)==0 )dp[group|dst][v] = Math.min(dp[group][u] + dis[u][v], dp[group|dst][v]);}int minDis = 0x3f3f3f3f;for (int i=0; i<n; i++)minDis = Math.min(dp[(1<<n)-1][i], minDis);return minDis;}public static void main(String[] args){int[][] graph = new int[4][];graph[0] = new int[]{1,2,3};graph[1] = new int[]{0};graph[2] = new int[]{0};graph[3] = new int[]{0};int r = new ShortestPathVisitingAllNodesDP().shortestPathLength(graph);System.out.println(r);}
}
 
代碼
  
  關于FLoyd算法的介紹參考文章。
  用二維數組記錄任意兩點之間的距離。dis[i][j]表示從i節點到j節點的距離。如果這兩點之間有邊,則dis的初始值是邊的權重,否則是無窮大。
  如果要讓兩點之間(例如a,b)的路程變短,那只能引入第三點(k)。如果a->k->b的距離小于a->b的距離,那就引入k。有時候可能需要引入不止一個節點,才能找到ab之間的最短路徑:a->k1->k2…->b。每個頂點可能使得另外兩個節點路程變短。
  第二步,如果允許在所有節點之間跨越節點1。如果dis[i][1]+dis[1][j]<dis[i][j]dis[i][1]+dis[1][j]<dis[i][j]dis[i][1]+dis[1][j]<dis[i][j],那么dis[i][j]=dis[i][1]+dis[1][j]dis[i][j]=dis[i][1]+dis[1][j]dis[i][j]=dis[i][1]+dis[1][j]。需要用兩個for循環實現替換。
  第三步,如果允許在所有節點之間跨越節點1、2。如何做呢?我們需要在只允許經過1號頂點時任意兩點的最短路程的結果下,再判斷如果經過2號頂點是否可以使得i號頂點到j號頂點之間的路程變得更短。即判斷e[i][2]+e[2][j]是否比e[i][j]要小。
  第四步,進一步計算允許跨越節點1,2,3…,一直到節點n。最后的代碼就是:
 
public void floyd(int n) {for(int k=0; k<n; k++)for(int i=0; i<n; i++)for(int j=0; j<n; j++)dis[i][j] = Math.min(dis[i][j], dis[i][k]+dis[k][j]);}
 
                            總結
                            
                                以上是生活随笔為你收集整理的847. Shortest Path Visiting All Nodes(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                            
                                如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。