dijkstra是一种单元最短路径算法,其能在较好时间复杂度内处理这一问题。但其对负权圈的处理让人不太满意——会陷入死循环
其思想和Prim算法差不多,都是贪心。
把图中的所有点划分为两个集合:包含远点S和不包含原点S的
每次从不包含原点S的集合中找出一个离原点S最近的点(这样就没有点能够比这个点更加接近原点,这也是其不能处理负权边的原因)
我们先考虑简单一点的情况:没有负权边
设u是不包含s中dist最小的那个点,另外v是不包含S中的任意点
如果v能更新u点<==>dist[v]+map[v][u]<dist[u];因为map[v][u]>0所以有dist[v]<dist[u]假设不成立
所以我们就有一个算法啦
1.每次找出不包含S中最近点,加入包含s的集合
2.维护所有和这个店相连的不在包含s的集合里的点到原点的距离(Prim维护的是到包含S的集合的距离)
时间复杂度Θ(n^2)比较优,适合稠密图。
但我们发现每次找一个最近点有点耗时,因为要支持减值和求最小的操作,就用优先队列啦
(优先队列在小根堆情况下支持降值,大根堆下支持升值)但优先队列里的元素有O(E)个开空间时需注意~~
上代码~
转自HK大神的blog,对于重载()不是很懂
#include<iostream> #include<cstdio> #include<queue> using namespace std; int n,m,S,tot,Next[500010],head[20000],tree[500010],val[500010]; bool visit[20000]; long long dis[20000]; struct cmp { bool operator()(int a,int b) { return dis[a]>dis[b]; } }; priority_queue<int,vector<int>,cmp> Q; void add(int x,int y,int z) { tot++; Next[tot]=head[x]; head[x]=tot; tree[tot]=y; val[tot]=z; } int main() { scanf("%d%d%d",&n,&m,&S); tot=0; for (int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); if (x==y) continue; add(x,y,z); } for (int i=1;i<=n;i++) { visit[i]=false; dis[i]=2147483647; } Q.push(S); dis[S]=0; while (!Q.empty()) { int u=Q.top(); Q.pop(); if (visit[u]) continue; visit[u]=true; for (int i=head[u];i;i=Next[i]) { int v=tree[i]; if (!visit[v]&&dis[v]>dis[u]+(long long)val[i]) { dis[v]=dis[u]+val[i]; Q.push(v); } } } for (int i=1;i<=n-1;i++) printf("%lld ",dis[i]); printf("%lld ",dis[n]); return 0; }
下面自己写的,渣~(我会告诉你我懒)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #define N 2000005 using namespace std; int n,m,s,next[N],to[N],head[N],vis[N],cos[N],dis[N],num=0,u,v,c; struct note{ int to,cos; }; struct cmp { bool operator()(int a,int b) { return dis[a]>dis[b]; } }; priority_queue<int,vector<int>,cmp> Q; void push_way(int u,int v,int c) { to[++num]=v; cos[num]=c; next[num]=head[u]; head[u]=num; } void dijkstra(int s) { Q.push(s); //vis[s]=1; dis[s]=0; while(!Q.empty()) { int u=Q.top(); Q.pop(); if(vis[u]) continue; cout<<u<<endl; vis[u]=1; for(int i=head[u];i;i=next[i]) { int v=to[i]; if(!vis[v]&&dis[v]>dis[u]+cos[i]) { dis[v]=dis[u]+cos[i]; Q.push(v); } } } for(int i=1;i<=n;i++) { cout<<dis[i]<<' '; } cout<<endl; } int main() { scanf("%d %d %d",&n,&m,&s); for(int i=1;i<=n;i++) dis[i]=10000; for(int i=1;i<=m;i++) { scanf("%d %d %d",&u,&v,&c); push_way(u,v,c); } dijkstra(s); }