最短路問題的擴充套件與應用

2022-08-20 12:03:14 字數 3503 閱讀 8633

下面簡要介紹k短路、差分約束系統、有向無環圖上的最短路和floyd演算法求最小環的求解方法。

1.k短路

k短路就是指次短路、第三最短路……等問題。其在實際生活中有頗多用處。

其基本演算法為:

目前使用較多的演算法是單源最短路配合a*。a*是搜尋中比較高階的方式,a*演算法結合了啟發式搜尋(這種方法通過充分利用圖給出的資訊來動態地做出決定而使搜尋次數大大降低)和形式化方法(這種方法不利用圖給出的資訊,而僅通過數學的形式分析)。他通過乙個估價函式f(h)來估計圖中的當前點p到終點的距離,並由此決定他的搜尋方向,當這條路徑失敗時,他會嘗試其他路徑。對於a*,估價函式=當前值+當前位置到終點的最短距離,即f(p)=g(p)+h(p),每次擴充套件估價函式值最小的乙個。對於k短路演算法來說,g(p)為當前從s到p所走路徑的長度,h(p)為從點p到t的最短路徑的長度,則f(p)的意義就是從s按照當前路徑走到p後再走到終點t一共至少要走多遠。也就是每次擴充套件都是有方向的,這樣對提高出解的速度還是降低擴充套件的狀態數目都有好處。為了加速計算,h(p)需要在a*搜尋之前進行預處理,只要將原圖的所有邊反向,再從終點t做一次單源最短路就能夠得到每個點的h(p)了。

具體實現如下:

採用鏈式前向星來存圖。

1

struct

node1210

};11

12bool spfa(int s,int n,int head[maxn],node edge[maxm],int

dist[maxn])

1314

int a_star(int start,int end,int n,int k,int head[maxn],node edge[maxm],int

dist[maxn])

1533

if(cnt==k)

3437

for(int i=head[e.to];i!=-1;i=edge[k].next)

3844}45

return -1;46

}47intmain()

48

view code

spfa+a*求k短路應該說是比較高效的一種演算法,但是比較遺憾的是由於a*的特殊性,他的效率無法計算。

note:當s=t時,k=k+1;因為s到t的這條距離為0的路不能算在這k短路中。

2.差分約束系統

差分約束系統是線性規劃問題的一種。在乙個差分約束系統中,線性規劃矩陣a的每一行包含乙個1和乙個-1,a的所有其他元素都為0.因此,由ax≤b給出的約束條件是m個差分約束集合,其中包含n個未知元。每個約束條件為如下形式的簡單線性不等式:xj-xi≤bk,其中1≤i,j≤n,1≤k≤m。

基本演算法:

通過觀察不難發現:每個約束條件的不等式與求單源最短路徑演算法中的鬆弛操作極為相似。

將圖形理論與差分約束系統ax≤b加以聯絡:m•n的線性規劃矩陣a可被看做n個頂點、m條邊的圖的關聯矩陣(的轉置。對於每個頂點vi對應著n個未知量中的乙個xi。圖中的每個有向邊對應於兩個未知變數的m個不等式的其中乙個。

這樣,通過求解新建圖的單源最短路徑問題就能得到差分約束系統的一組解。

為保證圖的連通,在圖中引入附加節點vs,使得圖中每個頂點都能從vs可達,並設弧的權值為0.對於每個差分約束xj-xi≤bk,則弧i,xj>的權為bk。

初始化dist[vs]=0,dist[vi]=inf(i≠s)。

求解以vs為源點的單源最短路徑。此時使用的演算法一般為spfa演算法,因為差分約束系統中一般都存在負值。另外在這裡有個技巧,就是如果使用spfa的時候不想新增附加節點的話,可以在初始化的時候把所有節點都加入佇列中,其實就是相當於源點入隊,開始演算法後vs出隊更新所得到的佇列,因為沒有邊指向vs,所以後面的更新不會涉及vs。

需要說明的是:如果圖中存在負權迴路,則不存在可行解。vs如果到某點不存在最短路徑,則對於該點所表示的變數可以去任意值。

其實,該演算法在執行結束後vs到各點的距離就為一組可行解。

3.dag圖上的單源最短路徑問題

在無迴路的有向圖上,一定有某些點不能由其他的點所到達,如果有多個這樣的點,可以考慮加入乙個新的點u,使u到這些點有一條有向邊,並且權值為0,即可轉化為乙個源點的dag圖。這時有更高效的特殊解法。

基本演算法:

在dag上的單源最短路徑演算法,將用到拓撲排序的內容。dag圖一定存在拓撲排序,且若在有向圖g中從頂點vi到頂點vj有一條路徑,則在拓撲排序中頂點vi必須在vj之前,而因為在dag圖中沒有環,所以按照dag圖的拓撲排序進行序列的最短路徑的更新,一定能更新出最短路徑。處理頂點v時,對每條離開的邊執行鬆弛運算,如果給出從源點到u的一條更短路徑,則更新到u的最短路徑。這個過程將檢查從源點到圖中每個頂點的所有路徑,同時,拓撲排序按照正確的順序處理頂點。根差分約束系統一樣,對於多源點向單源點轉化時新加入的節點可以通過預處理來避免在實際**中加入這個節點,在dag圖上的單源最短路徑的**中,是將入度為0的節點的dist設為0,其他的設為inf。

note:dag上的單源最短路徑演算法和spfa演算法一樣,都相當於在bellman-ford演算法的基礎上,在點的更新順序上的特殊優化,所以dag上的單源最短路徑演算法也可以處理負權邊。

**如下:

1

//由拓撲排序演算法得到拓撲序列陣列queue,然後繼續下面的**

2int

dist[maxn];

3for(i=1;i<=n;i++)dist[i]=inf;

4 dist[1]=0;5

for(i=0;i)

612 }

view code

整個演算法的時間複雜度為o(m)。

4.floyd求最小環

基本演算法:

首先,理解floyd演算法是按照點的編號增加的順序更新最短路徑的,那麼如果存在這個最小環,會在這個環中的點編號最大的那個點u跟新最短路徑之前發現這個環,也就是說,當u點被拿來更新i到j的最短路徑時,可以發現這個閉合環路,發現的方法是更新最短路徑前,遍歷i、j點對,一定會發現某對i到j的最短路徑長度dist[i][j]+map[j][u]+map[u][i]!=inf,這時的i和j是當前環中挨著點u的兩個點。因為在之前的最短路徑更新過程中,u沒有參與更新,所以dist[i][j]所表示的路徑中不會有點u,所以如果上面的式子成立,一定是乙個環。而如果在每個新的點拿來更新最短路徑之前遍歷i和j驗證上面的式子,雖然不一定能遍歷圖中所有的環,但是由於dist[i][j]是i點到j點的最短路徑,會發現最小的環。之所以用dist[i][j]與點u來判斷,是因為同在這個最小環上的兩個點,他們之間的最短路徑一定是這個環的一部分。另外,這個演算法對於負環會失效,因為包含負環的圖上,dist[i][j]已經不能保證i到j的路徑上不會經過同乙個點多次了。

**如下:

1

int mincircle=inf;

2for(k=1;k<=n;k++)312

}13//floyd部分

14for(i=1;i<=n;i++)

1523}24

}25 }

view code

時間複雜度o(n^3)。

2014-02-22

最短路 最短路徑問題

題目描述 平面上有n個點 n 100 每個點的座標均在 10000 10000之間。其中的一些點之間有連線。若有連線,則表示可從乙個點到達另乙個點,即兩點間有通路,通路的距離為兩點直線的距離。現在的任務是找出從一點到另一點之間的最短路徑。input 共有n m 3行,其中 第一行為乙個整數n。第2行...

最短路問題

簡單的最短路問題 includeconst int maxnum 1002 const int maxint 100005 void dijkstra int n,int v,int dist,int c maxnum maxnum dist v 0 s v 1 依次將未放入s集合的結點中,取dis...

最短路問題

time limit 5000ms memory limit 65536kb total submit 16 accepted 3 page view 402submit status discuss description 現在有n個城市,編號從1到n。現在已知從城市i到城市j需要走的時間為aij...