5.5最短路徑

這個網址訪問不了了,在次特別找到了緩存,方便你們查閱:http://www.cs.ecnu.edu.cn/assist/js04/ZJS045/ZJS04505/zjs045050a.htmnode

5.5最短路徑 web

 

在一個圖中,兩個結點之間可能有多條路徑,且每條路徑上所通過的邊數多是不一樣的,若是是網絡,每條路徑的各邊權數之和也多是不一樣的。咱們一般把從一個指定的頂點到達另外一指定頂點的各邊權數之和爲最小的路徑稱爲最短路徑,這               類問題亦稱爲最短路徑問題。例如,用頂點表示城市,邊表示路徑,而邊上的權表示里程數,因而頂點、邊及里程數構成了一個有關城市的網絡,咱們從某一城市到達另外城市的最省路徑即是一個典型的最短路徑問題。                算法

[提升](增)數組

本節考慮的是帶權有向圖,除非特別聲明,不然有向邊上的權老是正的.咱們稱路徑的開始頂點爲源點,稱路徑的最後頂點爲終點.路徑的長度是該路徑上各邊的權之和. 緩存

 

 5.5.1求一個頂點到其餘各頂點的最短路徑網絡

[例子]               (增)app

給定帶權有向圖G=(V,E)E中每條弧(w,v)都有非負的權。指定V中的一個頂點v作源點,找從源點v到圖中其餘各頂點的最短路徑。 ide

始點               
終點               
最短路徑               
路徑長度
V1               
V2
(v1,v2)
10
V1               
V3
(v1,v4,v3)
50
V1               
V4
(v1,v4)
30
V1               
V5
(v1,v4,v3,v5)               
60
1->2   10
1->5 100
1->4   30
2->3   50
3->5   10
4->3   10
5->4   60
 
12345是節點,描述節點之間的鏈接和權

E. W Dijkstra最短路徑基本思想動畫

一個頂點V1做爲源點,求該頂點到其餘各頂點的最短路徑,E.               W Dijkstra提出了一個按路徑長度遞增的順序產生最短路徑的方法。此方法的基本思想是:把圖中全部結點分紅兩組,第一組包括已肯定最短路徑的頂點第二組包括還沒有肯定最短路徑的頂點               ,按最短路徑長度遞增的順序逐個把第二組的頂點加到第一組中,直到從V1出發能夠到達的全部頂點都已包括在第一組中。在這過程當中,總保持從V1到第一組各頂點的最短路徑都不大於從V1到第二組的任何頂點的最短路徑長度。另外,每一個頂點對應一個距離值,第一組的頂點對應的距離值就是從V1到此頂點的最短路徑長度,第二組的頂點對應的距離值是從V1到此頂點的只包括第一組的頂點爲中間頂點的最短路徑長度spa

具體的作法

一開始第一組只包括頂點V1,第二組包括其餘全部頂點。V1對應的距離值爲0,第二組的頂點對應的距離值是這樣肯定的:若圖中有弧〈V1,Vj〉,則Vj的距離爲此弧的權值,不然Vj的距離爲¥(或用一個很大的數表示)。而後每次從第二組的頂點中選一個其距離值爲最小Vm的加入到第一組中,每往第一組中加入一個頂點Vm,就要對第二組中的各個頂點的距離值進行一次修正。若加進Vm作中間結點,使從V1到Vj的最短路徑比不加Vm的路徑要短,則要修改Vj的距離值。修改後再選距離值最小的頂點加入到第一組中。如此進行下去,直到圖中全部頂點都包括在第一組中 ,或再也沒有可加入到第一組中的頂點存在爲止。

下面給出給出實現上面算法的程序

設有向圖用鄰接矩陣A表示,若〈V1,Vj〉是圖中的弧,則A[i,j]的值等於邊上所帶的權值,不然A[i,j]等於一個很大的正數MAX(在下面用&表示)。開始時A[i,j]=0表示頂點j未在第一組中,處理中用A[i,j]=1標誌第j個頂點已進入第一組。用數組ad存放鄰接矩陣A,數組元素的下標等於相關聯頂點序號減1。數組dist[n]的每一個元素爲頂點的距離值,pre[n]的每一個元素爲從V1到該頂點的路徑上此頂點的前一個頂點的序號,若從V1到該頂點無路徑,則用0做爲其前一個頂點序號。算法結束時,沿着頂點Vj對應的pre[j-1]追溯,就能肯定V1到Vj最短路徑,其最短路徑長度等於dist[j-1]。此算法起始頂點也能夠不是V1,起始頂點的序號由變量k給出(即從頂點Vk出發)。源點爲Vk(其中k爲1)

 

 https://web.archive.org/web/20040118133953/http://www.cs.ecnu.edu.cn/assist/js04/Video_Audio/zm0450500.swf

 

 

 

完整程序

----------------------------

#include<stdio.h>

#define MAX 9999

void main()

{int ad[ ][MAX]; int k; int pre[MAX];         int dist[MAX];int n,i,j;

printf("pleas input ad[][]!");

prinf("input n");

scanf("%d",&n);

for(i=0;i<n;i++)

for(j=0;j<n;j++)

{scanf("%d",&ad[i][j]);)      

prinf("select Vk");

scanf("%d",&k)

dijkstra(ad,k,pre,dist,n);

}

 

void dijkstra (int ad[ ][], int k, int         pre[ ], int dist[ ], int n)

{

// 求有向圖以頂點Vk爲始點到其餘各項頂點間的最短路徑的dijkstra算法

int , j, p,min;

k=k-1;

for (i=0; i<n; i++) // 初始化

{

dist[i]=ad[k][i];

if (dist[i]<MAX)

pre[i]=k+1;

else

pre[i]=0;

}

pre[k]=0;

dist[k]=0; // 數組dist和pre初始化

ad[k][k]=1; // Vk加入到第一組

for (j=0; j<=(n-1); j++)

{

min=MAX;

p=-1;

for(i=0;i<=(n-1);i++)

if(ad[i][i]= =0 && dist[i]<min)         

{

p=i;

min=dist[i];

} // 在第二組中選距離值最小的頂點

if (p = = -1) break; // 以沒有頂點可往第一組加

else

 

ad [p][p]=1;

// 把第二組中選出的距離最小的頂點加到第一組中

for {i=0; i<m; i++}

if (ad[i][i] = =0 && (min+ad[p][i]<dist[i])         

{

dist[i]=min+ad[p][i];

pre[i]=p+1;

}

// 更新第二組中的當前距離值和路徑上前以頂點序號

}

}

 

-------------------

實際上,咱們也能夠用鄰接表來存儲圖。下面給出採用鄰接表存儲結構的Dijkstra算法

 

 

----------------------------------------------

 

void shortpath (head, n,         m, s)

 

struct headnode had[100];

 

int n, m, s[100];

 

{

 

int * visit, q[100], front, rear, h,         i, j, k;

 

struct node * p;

 

visit=malloc(n * sizeof (int));

 

for (k=0; k<n; k++)

 

visit[k]=0;

 

front=0;

 

rear=0;

 

for (k=0; k<n; k++)

 

s[k]=-1;

 

visit [m-1]=1;

 

s[m-1]=0;

 

q[rear]=m;

 

rear=rear+1;

 

while (front!=rear)

 

{

 

k=q[front];

 

front=front+1;

 

p=head[k-1].link;

 

while (p!=NULL)

 

{

 

j=p->vertex;

 

h=s[k-1]+p->weight;

 

if((s[j-1] = =-1)||(s[j-1]>h))

 

{

 

s[j-1]=h;

 

if (visit[j-1]= =0)

 

{

 

visit[j-1]=1;

 

q[rear]=j;

 

rear=rear+1

 

}

 

}

 

p=p->next;

 

}

 

}

 

}

 

------------------------------------------------------------

 

 

5.5.2    求每一對頂點之間的最短路徑

 

 

利用Dijkstra算法實現            

要求一個有n個頂點的帶權有向圖的每一對頂點之間的最短路徑,顯而易見的解決方法是:以某一頂點爲起始點,用Dijkstra算法求該點到其餘頂點的最短路徑,而後不斷變換起始點,再用Dijkstra算法求解,即經過n次重複執行Dijkstra算法來求得每一對頂點之間的最短路徑。            

但這種方法比較繁瑣,下面介紹一種簡便的方法               

Floyd算法            

弗洛伊德(R. W Floyd)提出了另外一種算法,這種算法仍用鄰接矩陣A表示帶權有向圖。若是從Vi到Vj有弧,則從Vi到Vj存在一條長度爲A[i,j]的路徑,該路徑不必定是最短路徑,須要進行n次試探。            

具體方法

爲了方便描述試探的過程,先引入k-path的定義。定義k-path爲圖中任意一條從頂點u到w的、中間頂點序號不大於k的路徑。好比〈u,V3,w〉至少是u到w的3-path,由於中間頂點的序號是3,固然也可認爲是4-path,5-path等。若此路徑存在,則u到w的0-path爲〈u,w〉。               

假設已知從u到w的最短的(k-1)-path,則最短的k-path要私通過序號爲k的頂點Vk,要麼不通過。若是u到w的最短的k-path通過頂點Vk則它的一部分是從u到Vk的(k-1)-path,另外一部分爲Vk到w的(k-1)-path;不然,保持最短的u到w的(k-1)-path不變(即u到w的最短的k-path與其最短的(k-1)-path相同)。這樣,通過n次試探,獲得的u到w的最短的n-path爲任意兩點u到w的最短路徑。圖的鄰接矩陣即表示了試探的初始狀態。由w,v的任意性,可獲得圖中每一對頂點之間的最短路徑            

 

下面給出求解每一對頂點之間的最短路徑的程序(程序中M=9999;動畫中用∝表示)

ad數組存放圖的鄰接矩陣。另外一個矩陣path[i,j]是從Vi到Vj的最短的k-path上Vj的前一個頂點的序號(k=1,2,…,n),約定若Vi到Vj無路徑時path[i,j]=0。求得最短路徑後由path[i,j]的值追溯,能夠獲得從Vi到Vj的最短路徑。用p數組存放path矩陣,算法中全部數組的數組元素下標等於相關連點的序號減1,相應地k從0至(n-1)。

 

2->1  3

1->2  8

1->3  5

3->2  2

    |0     8     5|

ad=|3     0     ∝|

     |∝     2     0|

 

 

�������������這個帶權有向圖及其代價鄰接矩陣就是咱們的示範例子

Floyd算法

#include<stdio.h>

#define MAX 9999

void main()

{int ad[ ][MAX]; int k; int p[MAX]; int         n,i,j;

printf("pleas input ad[][]!");

prinf("input n");

scanf("%d",&n)

for(i=0;i<n;i++)

for(j=0;j<n;j++)

{scanf("%d",&ad[i][j]);}      

prinf("select Vk");

scanf("%d",&k)

floyd(ad,p,n);

}

void floyd(ad, p, n)

int ad[ ][M], p[ ][M], n; //ad存放鄰接矩陣 p存放最短路徑通過的頂點

int i, j, k;

for (i=0; i<n; i++)

for (j=0; j>n; j++)

{

if (i= =j) p[i][j]=0; // 頂點自己假設無路徑

else

if (ad[i][j]<M) p[i][j]= i+1; // 置p的初始值

else p[i][j]=0;

}

for (k =0; k<n; k++) // 利用三重循環將各狀況窮舉

for ( i=0; i<n; i++)

for (j=0; j>n; j++)

if (ad[i][k]+ad[k][j]<ad[i][j]) // 加入中間頂點k來試探

{

ad[i][j]=ad[I][k]+ad[k][j]; // 從新置兩點間最短路徑

p[i][j]=p[k][j]; // 將k加入p

}

}

相關文章
相關標籤/搜索