Dijkstra
Dijkstra算法適用於邊權爲正的情況,用於計算正權圖上的單源最短路(Single-Source Shortest Paths,SSSP),即從單個源點出發,到所有結點的最短路。該算法同時適用於有向圖和無向圖。時間複雜度爲O(n^2),n是節點數。
算法思想:
首先初始化dist爲INF=0x3f3f3f3f,dist[源點]=0,然後循環n次,在所有未標號的結點中,選出dist值最小的結點x,給結點x標記,對從x結點出發的所有邊(x,y)更新dist[y]。
該算法也能很方便的打印出結點1到所有結點的最短路本身,只需在更新dist[y]時用path數組維護從最短路到該結點的父結點的下標即可。
現在算法競賽中可能一般的Dijkstra寫法會被卡時間,n^2的複雜度還是很高的。所以我們寫正權的最短路問題最好還是用鄰接表+堆優化(優先隊列優化)的Dijkstra。
Dijksta算法中,如果我們採用的是鄰接矩陣來存邊,空間浪費大,時間複雜度也高,所以我們考慮優化它,採用鄰接表來存儲,其次我們可以用優先隊列來排序大小,其時間複雜度大大降低。
我看網上有些人是自己手寫小根堆實現,這當然是更好的。我問了下學長,應該是不會卡手寫小根堆和STL優先隊列之間的差距,而優先隊列的寫法更簡潔,所以我還是選優先隊列來寫。
我以hdu2066爲模板來實現優先隊列優化的Dijkstra
hdu2066
這題不是很難,把根看成源點,將其到車站的距離置爲0,然後直接Dijkstra,最後取所有終點的最小值即可。
具體優化過程和題解看代碼註釋
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e3+5;
int t,s,d;
struct Edge{
int to,len; //存儲邊信息,to是到的點,len是時間
};
vector<Edge> edge[maxn]; //edge[i]記錄與i點相連的邊
//把i點和dist[i]打包放入優先隊列。
//這裏要注意pair是按照第一個元素的大小排序,如果相同才按照第二個,所以我們要把dist[i]包裝在第一個元素上。
typedef pair<int,int> P;
int dist[maxn]; //dist[i]記錄從源點到i點的最短時間
void init() //初始化
{
for(int i=0;i<maxn;i++) edge[i].clear();
memset(dist,INF,sizeof(dist));
}
void Dijkstra()
{
priority_queue<P,vector<P>,greater<P> >pq;
P p;
Edge tmp;
dist[0]=0; //源點dist置0
pq.push(P(0,0)); //源點加入隊列
while(!pq.empty())
{
p = pq.top(),pq.pop();
int u = p.second; //得到當前節點
for(int i=0;i<edge[u].size();i++) //遍歷與當前節點相連的節點
{
tmp = edge[u][i];
if(dist[tmp.to]>dist[u]+tmp.len)
{
dist[tmp.to] = dist[u]+tmp.len; //更新
pq.push(P(dist[tmp.to],tmp.to));
}
}
}
}
int main()
{
while(~scanf("%d%d%d",&t,&s,&d))
{
init();
int a,b,c;
Edge tmp;
while(t--)
{
scanf("%d%d%d",&a,&b,&c);
//建邊
tmp.to = b,tmp.len = c;
edge[a].push_back(tmp);
tmp.to = a,tmp.len = c;
edge[b].push_back(tmp);
}
while(s--)
{
scanf("%d",&a);
//源點(家)和車站相連
tmp.to = a,tmp.len = 0;
edge[0].push_back(tmp);
tmp.to = 0,tmp.len = 0;
edge[a].push_back(tmp);
}
Dijkstra();
int ans = INF;
while(d--)
{
scanf("%d",&a);
if(dist[a]<ans) ans = dist[a]; //得到所有終點的最短時間
}
printf("%d\n",ans);
}
return 0;
}