最短路問題
一般都表現為求兩點之間的某種最小代價(如距離、時間、花費、成本等),可具體分為單源最短路和多源最短路,往往還要附加乙個前驅陣列記錄路徑。
單源最短路:
就是求某一點到其它所有點的最短路,這種問題我一般直接採用spfa+鄰接表,至於前向星優化的spfa沒寫過,寫過一次spfa+鏈式前向星可是wa掉了,所以我一般只採用鍊錶寫,而且寫多了也不覺得麻煩。spfa時間效率很高,為o(ke),可證明在絕大多數情況下k是乙個不超過2的常數,所以呢spfa演算法很快的,唯一的缺點我想應該就是不能做帶負環的圖(其它經典演算法都不能做負環的圖》~~<)。spfa與bfs很像,所以我經常寫錯,spfa與bfs的最大的區別就是每個點可能會多次進隊(因為某種次序的原因導致每個點會出現多次被更新的情況),而bfs每個點只能進一次隊,因此spfa具體實現時,一定要把剛出隊的點重新標誌為未訪問的點。spaf其實是bellman-ford的佇列優化,是直接在點上的鬆弛。
q[rear=front=1]=s;
p[s]=1;
d[s]=0;
while (front<=rear)
}++front;
}
至於bellman-ford演算法,我一般不用它來求最短路,但它可以方便的判斷乙個圖中是否存在負環,bellman-ford演算法可以處理負權圖,但同樣不能做帶負環的圖。它是在邊上的鬆弛,多趟對所有邊進行鬆弛判斷,有些邊不用鬆弛致使了大量冗餘判斷,這就是為啥其速度比不上spaf。具體實現時,因為是在邊上的鬆弛,所以最好採用邊集陣列存圖,而且這裡有個優化,就是如果某趟鬆弛判斷中沒有任何邊可鬆弛,即表明了最終結果已求出,可結束bellamn-ford的執行。
(問題背景:nyoj115 城市平亂~~)
#include#include#define max 1000000000
struct edgenodee[100000];
int num;
void addedge(const int &u,const int &v,const int &w)
int main()
} int min=max;
for (i=0;i
dijkstra演算法就更少寫了,其一其使用範圍窄,連負權的都不能處理,其二是樸素的dijkstra效率不高,大圖就掛了。。而採用最小堆優化或者優先佇列優化的dijkstra效率很高,能達到o(nlogn),最適合稠密圖了。
這裡指貼上最小堆優化的dijstra**:
#include#include#include#define maxn 50000
#define inf 0x7fffffff
struct edgenode;
struct vexnodeg[maxn];
struct heap;
heap heap[maxn*10];
int t,n,m,q,dis[maxn],len;
void insert(const heap &in)
heap[u]=in;
}const heap& pop()
if (heap[child].disnext)
if (!used[p->adjvex])
for (i=0;iadjvex=v;
s->weight=w;
s->next=g[u].firstedge;
g[u].firstedge=s;
} dijstra();
for (i=1;i<=t;++i)
if (dis[i]==inf) printf("no path\n");
else printf("%d\n",dis[i]);
// while (1);
return 0;
}
多源最短路:
可以採用spfa+前向星多次求單源最短路,時間效率為o(ve),還是很可以的~
flyod呢就很少用,除非圖比較小。我認為floyd最重要的是其中的思想,即dp。
flyod的dp方程是d[k,i,j]=min(d[k,i,j],d[k-1,i,k]+d[k-1,k,j]),
初始態為:
map[i,j] (i,j)∈e
d[0,i,j]=
map[i,j] (i,j)∈e
如果圖map後面不用的話,可以直接把d當成map用。由於階段k在狀態方程中是連續的,所以在具體實現時可採用二維陣列來迭代。
//普通的flyod
for (k=1;k<=n;++k)
for (i=1;i<=n;++i)
for (j=1;j<=n;++j)
if (d[i][j]>d[i][k]+d[k][j])
d[i][j]=d[i][k]+d[k][j];
//如果圖為無向圖的話,具體實現可以如下:
for (k=1;k<=n;++k)
for (i=1;id[i][k]+d[k][j])
d[i][j]=d[i][k]+d[k][j];
最短路徑拓展:
拓展1:就是利用flyod求最小環問題。思想還是dp思想,假設環的最大點編號為k,那麼對於無向圖來說,乙個環至少需要三個點,那麼假設與k直接相連的點編號為i、j,那麼
以k為最大編號的環的最小值是
circle[k,i,j]=min(circle[k,i,j],d[k-1,i,j]+map[k,i]+map[k,j])
拓展2:求出兩點最短路徑的數目
設p[i,j]表示i和j最短路徑數目,
那麼在用k來鬆弛後,p[i,j]=p[i,k]*p[k,j];
拓展3:
如何巧妙的判斷乙個點是否在i和j的最短路徑上,利用
if (d[i,k]+d[k,j]==d[i,j])在;
else 不在;
最短路總結
寫個部落格記錄一下最短路的幾種演算法,盡量做最正確的解答,減少大家的疑惑,網上有好多講的都抄來抄去,還有好多講的都是錯誤的。熟悉的最短路演算法就幾種 bellman ford,dijkstra,spfa,floyd,下面針對這幾個演算法具體解析一下。首先說明一點,就是關於負環的問題。bellman ...
最短路總結
穿越空間的限制,走最短的路找到你 u v之間的最短路滿足以下限制 對任意k g v,e 有 dist u,v dis u,k dis k,j 關鍵操作 鬆弛 void relax int i,int j,int k floyd void floyd 複雜度o v 3 可處理負環 拓展把所有邊存成負的...
最短路總結
首先是dij演算法,這是我第乙個掌握的最短路演算法!再來是bellman ford演算法,這個演算法比較容易理解,而且考慮到了負環的存在。記住,它對圖中的邊進行了 v 1次操作!首先,對d進行初始化 還有乙個spfa演算法 摘錄於學長空間 設立乙個先進先出的佇列用來儲存待優化的結點,優化時每次取出隊...