(原創)最短路徑-Dijkstra算法,以Til the Cows Come Home為例


 

 (1)首先先解釋一下單源最短路徑:

    1)容易的解釋:指定一個點(源點)到其余各個頂點的最短路徑,也叫做單源最短路徑

    2)官方解釋:給定一個帶權有向圖G=(V,E),其中每條邊的權是一個實數。另外,還給定V中的一個頂點,稱為源。現在要計算從源到其他所有各頂點的最短路徑長度。這里的長度就是指路上各邊權之和。這個問題通常稱為單源最短路徑問題。

 

2)解釋一下Dijkstra算法:

 

 

例如求A點到B、C、D、E、F頂點的最短路徑;

 

 

 

我們可以先這樣設想:

1)先把所有的點到另一個點的長度全部初始化為無窮大本身到本身則初始化為0再輸入數值;

第一:將所有點到點初始化為無窮大

 

 

 

代碼大致如下:

 1 const int INF = 0x3f3f3f3f;  //為無窮大;
 2 int G[2000][2000];
 3 int N  ;//N為點的個數;
 4 
 5         for( i = 1 ;i <= N ;i++)
 6         {
 7             for( j = 1 ;j <= N ;j++)
 8             {
 9                 G[i][j] = INF;
10             }
11         }

 

也可用下面這種:

1 #include<string.h>
2 const int INF = 0x3f3f3f3f;
3 int N; //點的個數;
4 int G[2000][2000];
5 memset(G,INF,sizeof(G));

 

 

第二:將本身到本身初始化為0 因為本身到本身的距離就為0

 

 

1 for(int i = 1 ;i <= N; i++)
2 {
3    G[i][i] = 0;    //本身到本身距離為0;
4 }

 

第三:輸入數據:

 

 

 

 

 

對應下面的表格:

 

 

代碼實現大概如下:

 

1 int M;        //M為邊的個數;
 2 int x  , y;  // x ,y 為同一邊的兩個點;
 3 int D;    //D 為題目給的邊的權值;
 4         for( i = 1; i <= M ;i++)
 5         {
 6             cin>>x>>y>>D;
 7            
 8                 if(G[x][y]>D)
 9                 {
10                     G[x][y] = D;
11                     G[y][x] = D;
12                 }
13            
14         }
15
16

 

17 //上面我們已經把點到其他點的距離初始化為無窮大,把本身到本身初始化為0;
   //那么上面這個循環的化,我們可以把題目給的兩點之間的權值輸入;

18 //題目給的權值一定為正值,所以比0大,故本身到本身的距離還是維持為0;
   //而小於無窮大,所以用上面那個循環可把邊的權值輸入;

   2)我們還需用一個一維數組d來存儲A頂點到其余各個頂點的初始路程

 

 

下面我們來模擬一下:

這是輸入最初數據的表格:

 

模擬:

 

 

1)既然是從A點到其余各個頂點的最短路徑,那就先找一個離A號頂點最近的頂點。  AB的距離最小

 

      

       所以d[2]的值就已經從估計值變成了確定值,目前離A頂點最近的是B頂點。

 

2既然選了B點,接下來看B頂點有哪些出邊呢,B->EB->C兩條邊。

 

 

先討論B->E能否讓A頂點到E頂點的路程變短;

A->B->E :d[2]+e[2][5]表示從ABE的距離;d[2]AB頂點的路程,e[2][5]表示BE的距離;  12+7 = 19;

A->E   :d[5] = 16;

我們發現 d[2]+e[2][5]=19d[5] = 16;    所以  d[5]  < d[2]+e[2][5];

所以AE目前的最短距離為16

     d    

 

 

因此d[5]更新為 16;(這個過程有個專業術語叫做:松弛);

d

 

 

 再判斷B->C

A->B->C:d[2]+e[2][3] 表示ABC的距離;d[2]AB頂點的路程,e[2][3]表示BC 的距離;12+10 = 22

A->C:A沒有直接到C ,所以為無窮大;

22<無窮大

所以AC目前的最短距離為22d[3] = 22;

 

 

這樣從AB的當前最短路徑就更新完了;

3)找除B外離A最近的點:由圖可知是F點;

 

 

 

 

 由圖可知F的出邊有兩條:F->E, F->D

先討論 F->E能否讓AE的距離變短;

A->F->E d[6]+e[6][5] = 14+ 9 = 23;

A->E :d[5] = 16;

所以d[5] < d[6]+e[6][5];

所以當前AE的最短距離為16  d[5] = 16;

 

 

 

再討論E->D的距離能否讓AD的距離變短

A->E->D  :   d[5]+e[5][4] = 16+2=18;

A->D   :   無窮大;

所以AD的當前最短距離更新為18 d[4] = 18;

 

 

 

4)找除BF外離A最近的點E

 

 

 

 E的出邊有CD,看E->CE->D能否使AC的距離變短,使AD的距離變短

先討論E->D

A->E->D d[5]+e[5][4] = 16+2 = 18

之前更新后的AD d[4] = 22;

所以AD 當前最短變為18 d[4] = 18;

 

 

 討論E->C,看是否讓AC變短

A->E->C d[5]+e[5][3] = 16+6 =22;

原更新的AC22

所以當前AC最短為22  d[3] = 22;

 

 

5)找除B,E,F外離A最近的點,D

 

 

 

 D的出邊只有D->C

D->C,能否讓AC變短;

A->D->C  d[4]+e[4][3] = 18+5 = 23;

AC   22

 所以AC當前最短為22  d[3] = 22;

 

 

 

6)最后剩下C

全部找完,確定了A到各個點的最短路徑;

下面以一道題為例

Bessie is out in the field and wants to get back to the barn to get as much sleep as possible before Farmer John wakes her for the morning milking. Bessie needs her beauty sleep, so she wants to get back as quickly as possible.


Farmer John's field has N (2 <= N <= 1000) landmarks in it, uniquely numbered 1..N. Landmark 1 is the barn; the apple tree grove in which Bessie stands all day is landmark N. Cows travel in the field using T (1 <= T <= 2000) bidirectional cow-trails of various lengths between the landmarks. Bessie is not confident of her navigation ability, so she always stays on a trail from its start to its end once she starts it.

Given the trails between the landmarks, determine the minimum distance Bessie must walk to get back to the barn. It is guaranteed that some such route exists.

Input

* Line 1: Two integers: T and N

* Lines 2..T+1: Each line describes a trail as three space-separated integers. The first two integers are the landmarks between which the trail travels. The third integer is the length of the trail, range 1..100.

Output

* Line 1: A single integer, the minimum distance that Bessie must travel to get from landmark N to landmark 1.

 這道題的題意就是:給定N個地點(編號從1N),T條道路,先輸入兩個正整數TN,接下來T行,每行三個整數,分別代表這條道路的起點和終點和長度,要求求出從N1的最短路徑;此題用Dijkstra算法(先懂得Dijkstra算法才能知道這道題怎么做)

代碼如下:  

解法一:未優化的Dijkstra ,時間復雜度為N²;

以上面的思想寫就好了,先找出最小,不斷更新“當前最小”,並注意標記哪些點已經被訪問過了,就是上面被塗紅的點;

 

 1 #include<iostream>
 2 #include<string.h>
 3 #include<stdio.h>
 4 using namespace std;
 5 
 6 
 7 
 8 const int INF = 0x3f3f3f3f;
 9 int N ,M;
10 int x , y;
11 int min1;
12 int D;
13 int flag ;
14 int vis[2005];          //用來記錄哪些“最近的點”被訪問過了;
15 int d[2005];           //用來記錄“當前最短距離”;
16 int G[2005][2005];        //輸入點與點間的距離;
17 int main()
18 {
19     while(scanf("%d%d",&M,&N)!=EOF)  //輸入邊的條數和點的個數;
20     {
21             for(int i = 1 ;i <= N ;i++)
22         {
23             for(int j = 1 ; j <= N ;j++)
24             {
25                 G[i][j] = INF;           //先將全部點到點的距離初始化為無窮大;
26             }
27         }
28     for(int i  = 1 ; i <= N ;i++)
29     {
30         G[i][i] = 0;                  //本身點到本身點距離初始化為0 ;
31     }
32     for(int i = 1 ; i <= M ;i++)
33     {
34         cin>>x>>y>>D;               //輸入題目給的點和兩點間的距離;
35         if(G[x][y]>D)    
36         {
37             G[x][y] = D;          //輸入數據;注意這是有向圖,所以要正反來一遍;有一些題目從A→B,和從B→A的距離不一樣;此題中A→B和B→A一樣;
38             G[y][x] = D;
39         }
40     }
41     memset(d,INF,sizeof(d));       //就是上面思路中的一位數組d ,用來記錄“當前最短距離”;每次更新;
42     d[1] = 0 ;          //先初始化d[1] = 0 ;起點1 到1 的距離為0;
43     
44     for(int i = 1 ; i <= N; i++)
45     {
46         min1 = INF;                      //每次找該點與其他點的最短距離時,先將min1初始化為無窮大;每次循環找新的點到其余點時,就需要min1初始化為INF;
如找i = 1 時與其連通距離最短的點,找到后min1 = d[1]; 后循環后找 i = 2 時與其連通距離最短的點時,應先將min1 再次賦為INF,找到的d[2]才對;
47 for(int j = 1 ; j<= N; j++) 48 { 49 if(min1>d[j]) 50 { 5152 if(vis[j]==0) //這是判斷沒有”被訪問過”的點;即還沒有被找到的“最近距離”的點;即上面思路中還沒被塗紅的點; 53 { 54 min1 = d[j]; //找到最小; 55 flag = j; //記錄該序號; 56 } 57 58 } 59 } 60 for(int k = 1 ; k <= N ;k++) 61 { 62 if(d[k]>d[flag]+G[flag][k]) //不斷更新“最近的點”; 63 d[k] = d[flag]+G[flag][k]; 64 } 65 vis[flag] = 1; //標記該點被訪問過了;即圖中“塗紅”點 66 67 } 68 cout<<d[N]<<endl; //直接輸出最短距離; 69 for(int i = 1 ; i <= N; i++) //多組輸入的話,則應將所有“塗紅的點”恢復;重新開始新的數據; 70 { 71 vis[i] = 0; 72 } 73 } 74 75 76 }

 

解法二:

 

下面是優化了的寫法,可以用優先隊列取優化;每次取最小的時候可以用優先隊列去取,時間復雜度優化到了NlogN;

 

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<queue>
 4 #include<string.h>
 5 using namespace std ;
 6 
 7 const int INF = 0x3f3f3f3f;
 8 int G[2000][2000];
 9 int d[2000];
10 
11 int i ,j;
12 
13 struct node{
14     int num;              //記錄結點的序號;
15     int dis;            //記錄結點的“最短距離”
16     friend bool operator<(node a ,node b)
17     {
18         return a.dis>b.dis;//運算符重載,優先隊列原本是從大到小輸出,現在改寫后變成從小到大;
19     }
20 };
21 
22 int main()
23 {
24     int M , N;
25     int x,y,D;
26     
27     priority_queue<node>que;//將等下的“最短路徑”入隊;
28     
29     
30     while(scanf("%d%d",&M,&N)!=EOF)
31     {
32         for( i = 1 ;i <= N ;i++)
33         {
34             for( j = 1 ;j <= N ;j++)
35             {
36                 G[i][j] = INF;//先全部初始化為無窮大;
37             }
38         }
39         
40             for(int i  = 1 ; i <= N ;i++)
41         {
42             G[i][i] = 0;
43         }
44         for( i = 1; i <= M ;i++)
45         {
46             scanf("%d%d%d",&x,&y,&D); //輸入數據;
47             
48                 if(G[x][y]>D)//如果比無窮大小就更新距離;即有數據的就有數據,無直接連通就是無窮大;
49                 {
50                     G[x][y] = D;
51                     G[y][x] = D;
52                 }
53             
54         }
55         memset(d,0x3f,sizeof(d));
56         d[1] = 0;
57         que.push({1,0}); //將起點入隊,起點和起點的距離是為0,因為是本身;
58         while(!que.empty())
59         {
60             node tp = que.top(); //每次取出隊列最前的數,即最小的數;
61 
62             que.pop();
63             
64             for(i = 1 ;i <= N ;i++)
65             {
66                 if(G[tp.num][i])//如果兩點間有距離
67                 {
68                     if(d[i]>d[tp.num]+G[tp.num][i])//每次更新“當前最短距離”
69                     {
70                         d[i] = d[tp.num] + G[tp.num][i];
71                         que.push({i,d[i]});//入隊;
72                     }
73                 }
74             }
75         }
76         
77         printf("%d\n",d[N]); //直接輸出1到N的最短距離;
78         while(!que.empty())  //注意隊列要清空;
79         {
80             que.pop();
81         }
82         
83         
84     }
85     return 0;
86 }

 

 

 

 

 


免責聲明!

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



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