堆优化的迪杰斯特拉
迪杰斯特拉算法
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;
}