Dijkstra算法模板


Dijkstra算法又稱為單源最短路徑,所謂單源是在一個有向圖中,從一個頂點出發,求該頂點至所有可到達頂點的最短路徑問題。 
      設G=(V,E)是一個有向圖,V表示頂點,E表示邊。它的每一條邊(i,j)屬於E,都有一個非負權W(I,j),在G中指定一個結點v0,要求把從v0到G的每一個接vj(vj屬於V)的最短有向路徑找出來(或者指出不存在)。
      Dijstra算法是運用貪心的策略,從源點開始,不斷地通過相聯通的點找出到其他點的最短距離
基本思想是:
      設置一個頂點的集合s,並不斷地擴充這個集合,一個頂點屬於集合s當且僅當從源點到該點的路徑已求出。開始時s中僅有源點,並且調整非s中點的最短路徑長度,找當前最短路徑點,將其加入到集合s,直到終點在s中。
基本步驟:
1、把所有結點分成兩組:
      第一組:包括已經確定最短路徑的結點;
      第二組:包括尚未確定最短路徑的結點。
2、開始時,第一組只包含起點,第二組包含剩余的點;
3、用貪心的策略,按最短路徑長度遞增的順序把第二組的結點加到第一組去,直到v0可達的所有結點都包含於第一組中。在這個過程中,不斷更新最短路徑,總保持從v0到第一組各結點的最短路徑長度dist都不大於從v0到第二組任何結點的路徑長度。
4、每個結點對應一個距離值,第一組結點對應的距離就是v0到此結點的最短路徑長度,第二組結點對應的距離值就是v0由第一組結點到此結點的最短路徑長度。
5、直到所有的頂點都掃描完畢(v0可達的所有結點都包含於第一組中),找到v0到其它各點的所有最短路徑。

 動畫演示:http://www.jcc.jx.cn/kejiandb/Dijkstra.swf

 如圖:求0點到其他點的最短路徑。

(1)開始時,s1={v0},s2={v1,v2,v3,v4},v0到各點的最短路徑是{0,10,&,30,100};
(2)在還未進入s1的頂點之中,最短路徑為v1,因此s1={v0,v1},由於v1到v2有路徑,因此v0到各點的最短路徑更新為{0,10,60,30,100};
(3)在還未進入s1的頂點之中,最短路徑為v3,因此s1={v0,v1,v3},由於v3到v2、v4有路徑,因此v0到各點的最短路徑更新為{0,10,50,30,90};
(4)在還未進入s1的頂點之中,最短路徑為v2,因此s1={v0,v1,v3,v2},由於v2到v4有路徑,因此v0到各點的最短路徑更新為{0,10,50,30,60};
數據結構:
(1)用一個二維數組a[i..j,i..j]來存儲各點之間的距離,10000表示無通路:
(2)用數組dist[i..j]表示最短路徑;
(3)用集合s表示找到最短路徑的結點。 

  1 /***************************************
  2 * About:    有向圖的Dijkstra算法實現
  3 * Author:   Tanky Woo
  4 * Blog:     www.WuTianQi.com
  5 ***************************************/
  6  
  7 #include <iostream>
  8 using namespace std;
  9  
 10 const int maxnum = 100;
 11 const int maxint = 999999;
 12  
 13 // 各數組都從下標1開始
 14 int dist[maxnum];     // 表示當前點到源點的最短路徑長度
 15 int prev[maxnum];     // 記錄當前點的前一個結點
 16 int c[maxnum][maxnum];   // 記錄圖的兩點間路徑長度
 17 int n, line;             // 圖的結點數和路徑數
 18  
 19 void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
 20 {
 21     bool s[maxnum];    // 判斷是否已存入該點到S集合中
 22     for(int i=1; i<=n; ++i)
 23     {
 24         dist[i] = c[v][i];
 25         s[i] = 0;     // 初始都未用過該點
 26         if(dist[i] == maxint)
 27             prev[i] = 0;
 28         else
 29             prev[i] = v;
 30     }
 31     dist[v] = 0;
 32     s[v] = 1;
 33  
 34     // 依次將未放入S集合的結點中,取dist[]最小值的結點,放入結合S中
 35     // 一旦S包含了所有V中頂點,dist就記錄了從源點到所有其他頂點之間的最短路徑長度
 36     for(int i=2; i<=n; ++i)
 37     {
 38         int tmp = maxint;
 39         int u = v;
 40         // 找出當前未使用的點j的dist[j]最小值
 41         for(int j=1; j<=n; ++j)
 42             if((!s[j]) && dist[j]<tmp)
 43             {
 44                 u = j;              // u保存當前鄰接點中距離最小的點的號碼
 45                 tmp = dist[j];
 46             }
 47         s[u] = 1;    // 表示u點已存入S集合中
 48  
 49         // 更新dist
 50         for(int j=1; j<=n; ++j)
 51             if((!s[j]) && c[u][j]<maxint)
 52             {
 53                 int newdist = dist[u] + c[u][j];
 54                 if(newdist < dist[j])
 55                 {
 56                     dist[j] = newdist;
 57                     prev[j] = u;
 58                 }
 59             }
 60     }
 61 }
 62  
 63 void searchPath(int *prev,int v, int u)
 64 {
 65     int que[maxnum];
 66     int tot = 1;
 67     que[tot] = u;
 68     tot++;
 69     int tmp = prev[u];
 70     while(tmp != v)
 71     {
 72         que[tot] = tmp;
 73         tot++;
 74         tmp = prev[tmp];
 75     }
 76     que[tot] = v;
 77     for(int i=tot; i>=1; --i)
 78         if(i != 1)
 79             cout << que[i] << " -> ";
 80         else
 81             cout << que[i] << endl;
 82 }
 83  
 84 int main()
 85 {
 86     freopen("input.txt", "r", stdin);
 87     // 各數組都從下標1開始
 88  
 89     // 輸入結點數
 90     cin >> n;
 91     // 輸入路徑數
 92     cin >> line;
 93     int p, q, len;          // 輸入p, q兩點及其路徑長度
 94  
 95     // 初始化c[][]為maxint
 96     for(int i=1; i<=n; ++i)
 97         for(int j=1; j<=n; ++j)
 98             c[i][j] = maxint;
 99  
100     for(int i=1; i<=line; ++i)  
101     {
102         cin >> p >> q >> len;
103         if(len < c[p][q])       // 有重邊
104         {
105             c[p][q] = len;      // p指向q
106             c[q][p] = len;      // q指向p,這樣表示無向圖
107         }
108     }
109  
110     for(int i=1; i<=n; ++i)
111         dist[i] = maxint;
112     for(int i=1; i<=n; ++i)
113     {
114         for(int j=1; j<=n; ++j)
115             printf("%8d", c[i][j]);
116         printf("\n");
117     }
118  
119     Dijkstra(n, 1, dist, prev, c);
120  
121     // 最短路徑長度
122     cout << "源點到最后一個頂點的最短路徑長度: " << dist[n] << endl;
123  
124     // 路徑
125     cout << "源點到最后一個頂點的路徑為: ";
126     searchPath(prev, 1, n);
127 }

輸入數據:
5
7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60
輸出數據:
999999 10 999999 30 100
10 999999 50 999999 999999
999999 50 999999 20 10
30 999999 20 999999 60
100 999999 10 60 999999
源點到最后一個頂點的最短路徑長度: 60
源點到最后一個頂點的路徑為: 1 -> 4 -> 3 -> 5

最后給出兩道題目練手,都是直接套用模版就OK的:
1.HDOJ 1874 暢通工程續
http://www.wutianqi.com/?p=1894

2.HDOJ 2544 最短路
http://www.wutianqi.com/?p=1892

 

 

 模板2:

  1 /*Dijkstra求單源最短路徑 2010.8.26*/
  2  
  3 #include <iostream>
  4 #include<stack>
  5 #define M 100
  6 #define N 100
  7 using namespace std;
  8 
  9 typedef struct node
 10 {
 11     int matrix[N][M];      //鄰接矩陣 
 12     int n;                 //頂點數 
 13     int e;                 //邊數 
 14 }MGraph; 
 15 
 16 void DijkstraPath(MGraph g,int *dist,int *path,int v0)   //v0表示源頂點 
 17 {
 18     int i,j,k;
 19     bool *visited=(bool *)malloc(sizeof(bool)*g.n);
 20     for(i=0;i<g.n;i++)     //初始化 
 21      {
 22         if(g.matrix[v0][i]>0&&i!=v0)
 23         {
 24             dist[i]=g.matrix[v0][i];
 25             path[i]=v0;     //path記錄最短路徑上從v0到i的前一個頂點 
 26          }
 27         else
 28         {
 29             dist[i]=INT_MAX;    //若i不與v0直接相鄰,則權值置為無窮大 
 30             path[i]=-1;
 31         }
 32         visited[i]=false;
 33         path[v0]=v0;
 34         dist[v0]=0;
 35     }
 36     visited[v0]=true;
 37     for(i=1;i<g.n;i++)     //循環擴展n-1次 
 38      {
 39         int min=INT_MAX;
 40         int u;
 41         for(j=0;j<g.n;j++)    //尋找未被擴展的權值最小的頂點 
 42          {
 43             if(visited[j]==false&&dist[j]<min)
 44             {
 45                 min=dist[j];
 46                 u=j;        
 47             }
 48         } 
 49         visited[u]=true;
 50         for(k=0;k<g.n;k++)   //更新dist數組的值和路徑的值 
 51          {
 52             if(visited[k]==false&&g.matrix[u][k]>0&&min+g.matrix[u][k]<dist[k])
 53             {
 54                 dist[k]=min+g.matrix[u][k];
 55                 path[k]=u; 
 56             }
 57         }        
 58     }    
 59 }
 60 
 61 void showPath(int *path,int v,int v0)   //打印最短路徑上的各個頂點 
 62 {
 63     stack<int> s;
 64     int u=v;
 65     while(v!=v0)
 66     {
 67         s.push(v);
 68         v=path[v];
 69     }
 70     s.push(v);
 71     while(!s.empty())
 72     {
 73         cout<<s.top()<<" ";
 74         s.pop();
 75     }
 76 } 
 77 
 78 int main(int argc, char *argv[])
 79 {
 80     int n,e;     //表示輸入的頂點數和邊數 
 81      while(cin>>e>>n&&e!=0)
 82     {
 83         int i,j;
 84         int s,t,w;      //表示存在一條邊s->t,q權值為w
 85         MGraph g;
 86         int v0;
 87         int *dist=(int *)malloc(sizeof(int)*n);
 88         int *path=(int *)malloc(sizeof(int)*n);
 89         for(i=0;i<N;i++)
 90             for(j=0;j<M;j++)
 91                 g.matrix[i][j]=0;
 92         g.n=n;
 93         g.e=e;
 94         for(i=0;i<e;i++)
 95         {
 96             cin>>s>>t>>w;
 97             g.matrix[s][t]=w;
 98         }
 99         cin>>v0;        //輸入源頂點 
100          DijkstraPath(g,dist,path,v0);
101         for(i=0;i<n;i++)
102         {
103             if(i!=v0)
104             {
105                 showPath(path,i,v0);
106                 cout<<dist[i]<<endl;
107             }
108         }
109     }
110     return 0;
111 }


免責聲明!

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



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