最短路 dij floy spfa
例題 hdu1874 https://blog.csdn.net/murmured/article/details/18568657
一、Dijkstra
不可以算權值為負數的圖
Dijkstra單源最短路演算法,即計算從起點出發到每個點的最短路。所以Dijkstra常常作為其他演算法的預處理。
使用鄰接矩陣的時間複雜度為O(n^2),用優先佇列的複雜度為O((m+n)logn)近似為O(mlogn)
為樸素版本:
http://blog.51cto.com/ahalei/1387799
還有一個 堆優化+鄰接表(鏈式前向星)優化版本:
const int maxn=1000005; const int inf=0x3f3f3f3f; struct Edge{ int v,w;//w為距離 int next; }; Edge edge[maxn];//邊編號 從1開始 struct qnode{ //堆優化 int u; //起點 int w;//距離 qnode(int u=0,int w=0):u(u),w(w){}//結構體過載 bool operator < (const qnode& a) const{ return w>a.w; } }; long long dis[maxn]; int head[maxn]; bool vis[maxn]; int x[maxn],y[maxn],z[maxn]; int n,m; int size; void add_edge(int u,int v,int w){//鄰接表加邊 edge[size].v=v; edge[size].w=w; edge[size].next=head[u]; head[u]=size; size++; } void dijkstra(int s){ priority_queue<qnode>q; while(!q.empty()) q.pop(); q.push(qnode(s,0)); dis[s]=0; while(!q.empty()){ qnode t=q.top(); q.pop(); int u=t.u; if(vis[u])continue; vis[u]=true;//找到一個點就標記一次 for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; int w=edge[i].w; if(!vis[v]&&dis[v]>dis[u]+w){ dis[v]=dis[u]+w; q.push(qnode(v,dis[v]));//存到隊堆裡會自動利用堆 進行排序; } } } }
{鄰接表:http://blog.51cto.com/ahalei/1391988
////其實我咋感覺 領接表 與 鏈式前向星一樣啊
二、floyd
可以解決傳遞閉包問題
可以處理邊是負數的情況,判斷圖中是否為有負圈,檢查是否存在dis[i][i]是否為負數
處理迴路(環)就看dis[i][i]。(Floyd 和 bellman-ford 都可已處理環)
任意兩點間的短路問題
三、SPFA(bellman-ford)
單源最短路徑 可以判斷負環
bellman-ford演算法的基本思想是,對圖中除了源頂點s外的任意頂點u,依次構造從s到u的最短路徑長度序列dist[u],dis2[u]……dis(n-1)[u],其中n是圖G的頂點數,dis1[u]是從s到u的只經過1條邊的最短路徑長度,dis2[u]是從s到u的最多經過G中2條邊的最短路徑長度……當圖G中沒有從源可達的負權圖時,從s到u的最短路徑上最多有n-1條邊
bellman-ford演算法 Bellman-ford 演算法:一個具有n個頂點的圖如果不存在環,則從頂點x,到頂點y,最多經過n-1條邊(要考慮連通性,每個頂點最多經過 1 次),因此 x 到 y 的最短路 最多經過 n - 1 次鬆弛操作(就是更新長度)就應該出現,如果第 n 次鬆弛還可以得到最優,那麼這個圖就肯定是存在環了(直接用Dijkstra 就無法得到最優的,環的存在會影響最優解是否存在)。
SPFA的實現如下:用陣列dis記錄更新後的狀態,cnt記錄更新的次數,佇列q記錄更新過的頂點,演算法依次從q中取出待更新的頂點v,按照dis(k)[u]的遞迴式計算。在計算過程中,一旦發現頂點K有cnt[k]>n,說明有一個從頂點K出發的負權圈,此時沒有最短路,應終止演算法。否則,佇列為空的時候,演算法得到G的各頂點的最短路徑長度。
多次入隊因為因為SPFA沒有向迪傑斯塔拉演算法那樣,尋找dist[]的最小值,所以重複入隊用所有結點來進行鬆弛,更新dis[]的最小值,因為這個點本身dis[]的變化,會影響到與之鄰接的點,所以要重複入隊。
判斷負環程式碼如下( 與不判斷只差兩行)
bool spfa()
{
for(int i=0;i<=n;i++)
dis[i]=INF;
bool vis[MAXN]={0};
int cnt[MAXN]={0};
queue<int> q;
dis[0]=0;
vis[0]=true;
cnt[0]=1;
q.push(0);
while(!q.empty())
{
int cur=q.front();
q.pop();
vis[cur]=false;
for(int i=head[cur];i!=-1;i=e[i].next)
{
int id=e[i].to;
if(dis[cur] + e[i].val > dis[id])
{
dis[id]=dis[cur]+e[i].val;
if(!vis[id])
{
cnt[id]++;
if(cnt[cur] > n) //判斷負環
return false; //結束函式
vis[id]=true;
q.push(id);
}
}
}
}
return true;
}