Floyd最小環


 

本文轉自這里

 

 

  最小環:從一個點出發,經過一條簡單路徑回到起點成為環.圖的最小環就是所有環中長度最小的.

 

  怎樣求最小環呢?

 

  1傳統的解決方法(dijkstra):
        任意一個最小環環的權值,我們都可以看成兩個有邊相連的結點i、j的直接距離加上i、j間不包含邊(邊i->j)的最短路徑。求最短路徑我們第一個想到的就Dijkstra算法。而Dijkstra所求的是一個點到所有點的最短距離。用Dijkstra所求的i、j的最短距離一定是i、j的直接距離(如果i,j連通),所以我們需要先將i、j的邊從圖中刪除(若i,j不連通,則不用刪除),再用Dijkstra求新圖中i、j的最短距離即可。所以我們每次在圖中選取一條邊,把它從圖中刪掉.然后對刪掉的那條邊所對應的2點進行Dijkstra,也就是m次Dijkstra。

 

  2.floyd求最小環:

 

        拋開Dijkstra算法,進而我們想到用Floyd算法。我們知道,Floyd算法在進行時會不斷更新矩陣dist(k)。設dist[k,i,j]表示從結點i到結點j且滿足所有中間結點,它們均屬於集合{1,2,⋯ ,k}的一條最短路徑的權。其中dist[0,i,j ]即為初始狀態i到j的直接距離。對於一個給定的賦權有向圖, 求出其中權值和最小的一個環。我們可以將任意一個環化成如下形式:u->k->v ->(x1-> x2-> ⋯ xm1)-> u(u與k、k與v都是直接相連的),其中v ->(x1-> 2-> ⋯ m)-> u是指v到u不經過k的一種路徑。

 

        在u,k,v確定的情況下,要使環權值最小, 則要求 (x1一>x2->⋯一>xm)->u路徑權值最小.即要求其為v到u不經過k的最短路徑,則這個經過u,k,v的環的最短路徑就是:[v到u不包含k的最短距離]+dist[O,u,k]+dist[O,k,v]。我們用Floyd只能求出任意2點間滿足中間結點均屬於集合{1,2,⋯ ,k}的最短路徑,可是我們如何求出v到u不包含k的最短距離呢?
         現在我們給k加一個限制條件:k為當前環中的序號最大的節點(簡稱最大點)。因為k是最大點,所以當前環中沒有任何一個點≥k,即所有點都<k。因為v->(x1->x2->......xm)->u屬於當前環,所以x1,x2,⋯ ,xm<k,即x1,x2.⋯。xm≤k一1。這樣,v到u的最短距離就可以表示成dist[k一1 ,u,v]。dist[k一1,v,u]表示的是從v到u且滿足所有中間結點均屬於集合{1,2,⋯ ,k一1}的一條最短路徑的權。接下來,我們就可以求出v到u不包含k的最短距離了。這里只是要求不包含k,而上述方法用的是dist[k一1,v,u],求出的路徑永遠不會包含k+l,k+2,⋯ 。萬一所求的最小環中包含k+1,k+2,⋯ 怎么辦呢?的確,如果最小環中包含比k大的節點,在當前u,k,v所求出的環顯然不是那個最小環。然而我們知道,這個最小環中必定有一個最大點kO,也就是說,雖然當前k沒有求出我們所需要的最小環,但是當我們從k做到kO的時候,這個環上的所有點都小於kO了.也就是說在k=kO時一定能求出這個最小環。我們用一個實例來說明:假設最小環為1—3—4—5—6—2—1。的確,在u=l,v=4,k=3時,k<6,dist[3,4,1]的確求出的不是4—5—6—2—1這個環,但是,當u=4,v=6,k=5或u=5,v=2,k=6時,dist[k,v,u]表示的都是這條最短路徑.所以我們在Floyd以后,只要枚舉u.v,k三個變量即可求出最小環。時間復雜度為O(n3)。我們可以發現,Floyd和最后枚舉u,v,k三個變量求最小環的過程都是u,v,k三個變量,所以我們可以將其合並。這樣,我們在k變量變化的同時,也就是進行Floyd算法的同時,尋找最大點為k的最小環。

 

POJ 1734  

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define _Clr(x, y) memset(x, y, sizeof(x))
 5 #define INF 0xfffffff
 6 #define N 110
 7 using namespace std;
 8 
 9 int mat[N][N], dist[N][N];
10 int next[N][N]; // next[i][j]表示i到j經歷的第一個點。
11 int path[N];
12 int cnt, n;
13 
14 void Floyd()
15 {
16     int mins=INF;
17     for(int k=1; k<=n; k++)
18     {
19         for(int i=1; i<k; i++)
20         for(int j=i+1; j<k; j++)
21         {
22             int tmp = dist[i][j]+mat[i][k]+mat[k][j];
23             if(tmp < mins)// 更新最小環的權值
24             {
25                 mins = tmp;
26                 cnt=0;
27                 int p = i;
28                 while(p!=j) // 記錄最小環的路徑
29                 {
30                     path[cnt++] = p;
31                     p = next[p][j];
32                 }
33                 path[cnt++] = j;
34                 path[cnt++] = k;
35             }
36         }
37         for(int i=1; i<=n; i++)
38         for(int j=1; j<=n; j++)
39         {
40             if(dist[i][k]+dist[k][j] < dist[i][j])
41             {
42                 dist[i][j] = dist[i][k] + dist[k][j];
43                 next[i][j] = next[i][k];
44             }
45         }
46     }
47     if(mins==INF)
48         puts("No solution.");
49     else
50     {
51         for(int i=0; i<cnt; i++)
52             printf("%d%s", path[i], i==cnt-1 ? "\n":" ");
53     }
54 }
55 
56 void Init()
57 {
58     for(int i=1; i<=n; i++)
59     for(int j=1; j<=n; j++)
60     {
61         mat[i][j] = dist[i][j] = INF;
62         next[i][j] = j;
63     }
64 }
65 int main()
66 {
67     int m, a, b, c;
68     while(~scanf("%d%d", &n, &m))
69     {
70         Init();
71         while(m--)
72         {
73             scanf("%d%d%d", &a, &b, &c);
74             if(c < mat[a][b])
75             {
76                 mat[a][b] = mat[b][a] = c;
77                 dist[a][b] = dist[b][a] = c;
78             }
79         }
80         Floyd();
81     }
82     return 0;
83 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM