堆優化的迪傑斯特拉
迪傑斯特拉算法
rep(i, 1 , n-1){
int minx = inf;//對於每一次找點,都要找當前最短路的點
int pos;//記錄一下是哪個點
//**********優化點A*************
rep(j , 1 , n){
if(!vis[j] && dis[j] < minx){
minx = dis[j];
pos = j;
}
}
//pos頂點加入S集合
vis[pos] = 1;
//************優化點B*****************
//由於pos頂點的進入 對S集合以外的結點 其距離因為pos的加入需要進行更新
rep(k, 1 , n){
if(!vis[k] && dis[k] > dis[pos] + G[pos][k]){
dis[k] = dis[pos] + G[pos][k];
}
}
}
- 優化之處
- 優化點B :很容易想到,不必掃描1~n,只需遍歷由pos頂點可以到達的頂點即可,可以使用vector存儲圖,或者使用鏈式前向星
- 優化點A :事實上,每次找出一個距離源點最短的頂點,需要掃描1~n嗎?一個很朴素的想法是如果把這些點存起來然后按照距離關鍵字二分查找呢?
- 這里可以使用一個【優先隊列】,內部實現為小根堆,滿足動態的插入,在O(1)的時間內直接取出最小點,結合優化點A,使得總時間復雜度從O(V^2)降到O(VlogV+VE),對稀疏圖很有效果。
#include<iostream>
#include<queue>
#include<list>
#include<vector>
#include<cstring>
#include<set>
#include<stack>
#include<map>
#include<cmath>
#include<algorithm>
#include<string>
#include<stdio.h>
using namespace std;
typedef long long ll;
#define MS(x,i) memset(x,i,sizeof(x))
#define rep(i,s,e) for(int i=s; i<=e; i++)
#define sc(a) scanf("%d",&a)
#define scl(a) scanf("%lld",&a)
#define sc2(a,b) scanf("%d %d", &a, &b)
#define debug printf("debug......\n");
#define pfd(x) printf("%d\n",x)
#define pfl(x) printf("%lld\n",x)
const double eps=1e-8;
const double PI = acos(-1.0);
const int inf = 0x3f3f3f3f;
const ll INF = 0x7fffffff;
const int maxn = 1e3+10;
const int M = 1e4+10;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0 , 0};
int n,m;//頂點數 邊數
struct node{
int dis;//與源點的當前最小距離
int v;//頂點
node(){}
node(int vv, int d){v = vv; dis = d;}
};
bool operator < (node a, node b){
if(a.dis == b.dis) return a.v > b.v;
return a.dis > b.dis;
}
//鏈式前向星基本操作 也可以改成vector數組
int head[maxn];
int cnt;
struct star{
int to;
int w;
int nxt;
}edge[2*M];
void addEdge(int u, int v, int w){
edge[cnt].to = v;
edge[cnt].w = w;
edge[cnt].nxt = head[u];
head[u] = cnt++;
}
//記錄各個頂點到源點的最短距離
ll dist[maxn];
bool vis[maxn]; //記錄被選入S集合的頂點
//堆優化的迪傑斯特拉算法
void dijkstra(int s){
//建立優先隊列,優先關鍵字是到源點的距離
priority_queue<node> q;
rep(i , 1, n) dist[i] = inf,vis[i] = 0;
dist[s] = 0;
//初始結點入隊
q.push(node(s, 0));
while(!q.empty()){
//取出隊首 結點 這個點就是最短路徑點
node fr = q.top();
q.pop();
int v = fr.v;//頂點
int dis = fr.dis;//該結點到源點的最短距離
vis[v] = 1;//該頂點被選入S集合
//對v所能到達的頂點做松弛操作
for(int i=head[v]; i; i=edge[i].nxt){
int to = edge[i].to;
if(!vis[to] && dist[to] > dis + edge[i].w){
dist[to] = dist[v] + edge[i].w;
q.push(node(to , dist[to]));
}
}
}
}
int s,e;
int main(){
ios::sync_with_stdio(false);
while(cin>>n>>m){
cin>>s>>e;
cnt = 1;
MS(head , 0);
int u,v,w;
rep(i,1,m){
cin>>u>>v>>w;
addEdge(u , v , w);
addEdge(v , u , w);
}
dijkstra(s);
cout<<dist[e]<<endl;
}
return 0;
}