dijkstra是一种单源最短路径算法,即求一个点到其他点的最短路。不能处理负边权。
最近某种广为人知的算法频繁被卡,让dijkstra逐渐成为了主流,甚至在初赛中鞭尸了SPFA(?
dijkstra效率还是不错的,而且不容易被卡。
一、主要思想
用dis数组保存最短路径。初始化时,dis[s]设置为0(s为起点),其他无穷大。
枚举点i,每次从i能够到达的点中,选出权值最小的一个mpl(min place 个人叫法),把dis[mpl]直接设置为权值。可以证明,这就是最短的路径。
证明:若不是,则必有点k,能使
s->k+k->mpl < s->mpl
由权值最小得 s->mpl < s->k 明显矛盾
(我居然没写易证!
再枚举mpl到达的点k,若mpl->k+dis[mpl] > dis[k] 则更新dis[k]。专业术语叫做:松弛。
以此类推,更新完所有的点,算法结束。复杂度O(n2)
代码:
#include<iostream> #include<vector> #include<cstring> using namespace std; //GDT TQL Orz struct edge { int next,to,dis; } g[500005];//前向星 int dis[500005],gdt[500005],n,m,s,u,v,w,ne=0; //gdt就是head数组。此处为玩梗QwQ inline void add(int from,int to,int d) { g[++ne].next=gdt[from]; g[ne].to=to; g[ne].dis=d; gdt[from]=ne; }//前向星建边 inline void dijkstra() { int mn,mpl=s; bool vis[500005]= {0}; dis[s]=0; for(register int i=1;i<=n;i++) { mn=214748364; for(register int now=1; now<=n; now++) { if(dis[now]<mn&&!vis[now]) { mn=dis[now]; mpl=now; } } //选出权值最小的点 vis[mpl]=1; //此节点已被拓展,也就是变为白点(你们可能会从奇奇怪怪 //的算法书中得到这种说法)。 for(register int now=gdt[mpl]; now; now=g[now].next) { if(!vis[g[now].to]&&dis[g[now].to]>dis[mpl]+g[now].dis) dis[g[now].to]=dis[mpl]+g[now].dis; } } } int main() { memset(gdt,0,sizeof(gdt)); memset(g,0,sizeof(g)); cin>>n>>m>>s; for(int i=1; i<=n; i++) dis[i]=2147483647; for(int i=1; i<=m; i++) { cin>>u>>v>>w; add(u,v,w); } dijkstra(); for(int i=1; i<=n; i++) { cout<<dis[i]<<' '; } return 0; }
二、堆优化
所谓堆,实际相当于一个优先队列。
每次把点入队,使用时直接可以访问离s距离最小的。
具体实现时,会用STL中的priority_queue(优先队列)。而且,一个点需要一个地址(访问)和一个距离s的最短距离(比较、更新最短路径)。
代码:
#include<iostream> #include<vector> #include<cstring> #include<queue> using namespace std; //GDT TQL Orz struct edge { int next,to,dis; }g[500005]; struct node { int k,v; //地址key,权值value }; bool operator < (node n1,node n2) { return n1.v>n2.v; } //优先队列维护时需要比较,而结构体无法直接比较,使用重载运算符。 int dis[500005],gdt[500005],n,m,s,u,v,w,ne=0; inline void add(int from,int to,int d) { g[++ne].next=gdt[from]; g[ne].to=to; g[ne].dis=d; gdt[from]=ne; } inline void dijkstra() { int mn,mpl=s; node nd,temp; bool vis[500005]= {0}; dis[s]=0; priority_queue<node> pq; temp.k=s; temp.v=0; pq.push(temp);//第一个点更新起点 while(!pq.empty()) { nd=pq.top(); pq.pop(); if(vis[nd.k]) continue; vis[nd.k]=1;//拓展 mpl=nd.k; for(register int now=gdt[mpl]; now; now=g[now].next) { if(!vis[g[now].to]&&dis[g[now].to]>dis[mpl]+g[now].dis) { dis[g[now].to]=dis[mpl]+g[now].dis; temp.k=g[now].to; temp.v=dis[g[now].to]; pq.push(temp);//新的一个结点 } } } } int main() { memset(gdt,0,sizeof(gdt)); memset(g,0,sizeof(g)); cin>>n>>m>>s; for(int i=1; i<=n; i++) dis[i]=2147483647; for(int i=1; i<=m; i++) { cin>>u>>v>>w; add(u,v,w); } dijkstra(); for(int i=1; i<=n; i++) { cout<<dis[i]<<' '; } return 0; }
三、关于SPFA
如果一个图没有负边权,那就一定会卡你的SPFA。