最短路徑問題之迪傑特斯拉算法


  在C語言的實訓中,我學習到了一個自己以前曾經想學但是礙於水平不夠未學習的算法:迪傑特斯拉算法。通俗地說,就是解決最短路徑問題。相信大家都有過這樣的經歷:從a城市到b城市有一段距離,從a城市到c城市也有一段距離,從b,c城市到d城市都有一段距離,那么請問你要從a城市到d城市的最短距離是多少?相信只是那么幾個城市大家口算都能完成,但是,如果城市一多呢?並且問你的最短距離的數目也不止一段呢?很明顯,口算,心算就顯得有些不夠用了(天才排外),因此,掌握解決此類問題的算法就顯得十分重要,下面,我將通過兩道例題來講解單向最短路徑問題和雙向最短路徑問題。

例題1:單向最短路徑問題

 

 

  在圖中,我們可以明顯地看到,直線左右的123456分別代表六個城市,直線上面的1234567代表從一個城市到另一個城市的距離,並且這種距離是單向的。如果要求從1城市到5城市的最短路徑,相信大家一眼就能看出是從1城市直接到5城市的距離1,這似乎也有些太簡單了,我們來分析一下:能到達5城市的有1城市和4城市,那么,其實到5城市的最短距離可以表示為p(5) = min(p(1) + d15,p(4) + d45),其中,p(1),p(4)分別代表從1城市到11城市和從1城市到4城市的最短距離,d15和d45分別代表城市1到城市5的距離和城市4到城市5的距離。同理,我們設t(i)為從1城市到i城市的臨時距離,代表這個距離不一定是最短的距離,p(i)代表從1城市到i城市的最短距離,很明顯,p(1)為0,t(i)(i = 2,3 ....)開始要設置為一個很大的數字,因此,我們可以得到一下公式:t(i) = min(p(j) + dji,p(k) + dki),p(i) = min(t(i),t(i + 1),t(i + 2).....t(6));由此,我們可以知道,我們只需要遞推出t(i),再根據t(i)求出p(i)即可。在遞推中,我們要注意,公式t(i) = min(p(j) + dji,p(k) + dki)中的k城市和i城市必須有直接路徑。接下來上代碼。

代碼:

#include <stdio.h>
#define INF 999
int min(int a,int b)
{
return a < b ? a: b;
}
int main(void)
{
int p[7],l[7][7],T[7],i,j,flag,flag2[7][7] = {0},min1 = -1;
p[1] = 0;
l[1][3] = 6,l[3][4] = 3,l[4][5] = 4,l[4][6] = 7;
l[1][5] = 1,l[2][3] = 2;//將有直接路徑的城市的距離表示出來
flag2[1][3] = 1,flag2[3][4] = 1,flag2[2][4] = 1,flag2[4][5] = 1,flag2[4][6] = 1;
flag2[1][5] = 1,flag2[2][3] = 1;//用flag數組表示兩個城市是否有直接路徑
for (i = 2;i < 7;i++)
{
T[i] = INF;
}
for (i = 2;i < 7;i++)
{
for (j = 1;j < i;j++)
{
if(flag2[j][i])
{
T[i] = min(T[i],p[j] + l[j][i]);//求出臨時最短路徑
}
}
for (j = i;j < 7;j++)
{
if(min1 == -1 || min1 > T[j])
{
min1 = T[j];
flag = j;
}
}
p[flag] = min1;//求出最短路徑
min1 = -1;
}
//第一層大循環只是求得了從標號小的城市到標號大的城市的最短路徑,因此還需要第二次大循環來求得真正的最短路徑
for (i = 2;i < 7;i++)
{
for (j = 1;j < 7;j++)
{
if(flag2[j][i])
{
p[i] = min(p[i],p[j] + l[j][i]);
}
}
//需要特別注意的是,p[i]為999的值代表從1城市不能到達i城市
printf ("p[%d]=%d\n",i,p[i]);
}
printf ("%d\n",p[6]);

return 0;
}

例題2:雙向最短路徑問題

如圖:

 

 

圖略大,還請見諒,其實還是上面那一張圖,不過沒了箭頭,也就是既可以從1城市到5城市,也可以從5城市到1城市,那么同理,其實我們只需要在上面的代碼中加上從5城市到1城市的距離,以及其他可以互通的城市的距離,並且用flag數組表示他們有直接路徑,廢話不多少,直接上代碼。

代碼:

#include <stdio.h>
#define INF 999
int min(int a,int b)
{
return a < b ? a: b;
}
int main(void)
{
int p[7],l[7][7],T[7],i,j,flag,flag2[7][7] = {0},min1 = -1;
p[1] = 0;
l[1][3] = 6,l[3][4] = 3,l[4][5] = 4,l[4][6] = 7;
l[1][5] = 1,l[2][3] = 2;//將有直接路徑的城市的距離表示出來
l[3][1] = 6,l[4][3] = 3,l[5][4] = 4,l[6][4] = 7;
l[5][1] = 1,l[3][2] = 2;//將有直接路徑的城市的距離表示出來
flag2[1][3] = 1,flag2[3][4] = 1,flag2[4][5] = 1,flag2[4][6] = 1;
flag2[1][5] = 1,flag2[2][3] = 1;//用flag數組表示兩個城市是否有直接路徑
flag2[3][1] = 1,flag2[4][3] = 1,flag2[5][4] = 1,flag2[6][4] = 1;
flag2[5][1] = 1,flag2[3][2] = 1;//用flag數組表示兩個城市是否有直接路徑
for (i = 2;i < 7;i++)
{
T[i] = INF;
}
for (i = 2;i < 7;i++)
{
for (j = 1;j < i;j++)
{
if(flag2[j][i])
{
T[i] = min(T[i],p[j] + l[j][i]);//求出臨時最短路徑
}
}
for (j = i;j < 7;j++)
{
if(min1 == -1 || min1 > T[j])
{
min1 = T[j];
flag = j;
}
}
p[flag] = min1;//求出最短路徑
min1 = -1;
}
//第一層大循環只是求得了從標號小的城市到標號大的城市的最短路徑,因此還需要第二次大循環來求得真正的最短路徑
for (i = 2;i < 7;i++)
{
for (j = 1;j < 7;j++)
{
if(flag2[j][i])
{
p[i] = min(p[i],p[j] + l[j][i]);
}
}
//需要特別注意的是,p[i]為999的值代表從1城市不能到達i城市
printf ("p[%d]=%d\n",i,p[i]);
}
printf ("%d\n",p[6]);

return 0;
}

 


免責聲明!

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



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