自TG滚粗后咕咕咕了这么久,最近从新开始学OI,也会慢慢开始更博了。。。。node
最短路算法经典的就是SPFA(Bellman-Ford),Dijkstra,Floyd;ios
本期先讲两个经典的单源最短路算法:算法
首先是我最喜(hao)欢(xie)的SPFA(惋惜常常被卡)数组
SPFA:
Warning:SPFA在OI竞赛中慎用,极易容易被卡!!!优化
基本流程:
从起点开始,每次将扫到的点入队,每一个点遍历全部与其相连的点,并更新最短路,若是该点未入队,则将其入队;spa
均摊复杂度为$ O(KE) $(K=2),但由于SPFA在网格图中入队次数过多,致使卡成原始的Bellman-Ford($ o(VE) $),因此竞赛中不经常使用到(但仍是要学会的);code
丑陋的代码(洛谷P3371):blog
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int n,m,x,y,z,tot,f[500001],q[1000001],h,t,s,first[500001],nxt[500001],last[500001],en[500001],len[500001]; 5 //f为记录最短路径的数组,q为队列; 6 //first,nxt,last,en,len为平平无奇的邻接表; 7 bool b[10001]; 8 //判断是否在队列中 9 void add(int x,int y,int z) 10 { 11 ++tot; 12 if(first[x]==0) first[x]=tot; else nxt[last[x]]=tot; 13 en[tot]=y; 14 len[tot]=z; 15 last[x]=tot; 16 } 17 void SPFA(int x) 18 { 19 int k=first[x]; 20 do 21 { 22 if((long long)len[k]+f[x]<f[en[k]]) 23 { 24 f[en[k]]=len[k]+f[x]; 25 if(not b[en[k]]) 26 { 27 ++t; 28 q[t]=en[k]; 29 b[en[k]]=true; 30 } 31 } 32 k=nxt[k]; 33 } 34 while(k!=0); 35 } 36 int main() 37 { 38 scanf("%d%d%d",&n,&m,&s); 39 for(int i=1;i<=m;++i) 40 { 41 scanf("%d%d%d",&x,&y,&z); 42 add(x,y,z); 43 } 44 for(int i=1;i<=n;++i) 45 f[i]=2147483647; 46 f[s]=0; 47 h=0; 48 t=0; 49 SPFA(s); 50 while(h<=t) 51 { 52 SPFA(q[h]); 53 b[q[h]]=false; 54 ++h; 55 } 56 for(int i=1;i<=n;++i) 57 printf("%d ",f[i]); 58 return 0; 59 }
Dijkstra:
最经常使用的最短路算法(蒟蒻的我最近才学会太菜了);队列
基本流程:
定义两个点的集合,S为已求出最短路的点集,T为未求出最短路的点集;it
每次在T集合中找到一个Dis值最小的点,将其放入S集合中,并用它更新其余点的最短路;
由此,朴素的Dijkstra在每次选择操做中暴力枚举每一个点复杂度为$ O(V) $,更新时的复杂度也为$ O(V) $,及时间复杂度为$ O(V^2) $;
聪(AK)明(IOI)的读者可能已经发现了,选择Dis值最小的点时,暴力枚举过于浪费,为了不超时,咱们能够用堆(或者线段树)优化为$ o(Vlog(E)) $;
每次选择时,直接访问堆的根节点,将其弹出,并把更新的节点压入堆中;(每次压入堆中没必要要判是否已入堆,只须要开个bool数组记录该点是否已有最短路便可)
(甚至能够用斐波那契堆优化为$ o(E+Vlog(V)) $)
附上丑陋的代码(CodeChef CLIQUED):
1 #include<cstdio> 2 #include<queue> 3 #include<iostream> 4 using namespace std; 5 const int MAXN=1600010; 6 const long long INF=1e15+10; 7 struct node 8 { 9 int pos; 10 long long dis; 11 bool operator <(const node &x)const 12 { 13 return x.dis<dis; 14 } 15 }; 16 priority_queue<node> q; 17 int tot,first[MAXN],nxt[MAXN],last[MAXN],to[MAXN],len[MAXN]; 18 //平平无奇的邻接表 19 int T,n,k,x,m,s; 20 long long dis[MAXN]; 21 //dis为到每一个点的最短路径 22 bool vis[MAXN]; 23 //vis记录每一个点是否已有最短路的值 24 void dijistra() 25 { 26 dis[s]=0; 27 q.push((node){s,0}); 28 while(!q.empty()) 29 { 30 node t=q.top(); 31 int po=t.pos; 32 long long di=t.dis; 33 q.pop(); 34 //取堆的根节点 35 if(vis[po]) 36 continue; 37 //若是已有最短路,说明该点已更新过,无需更新 38 vis[po]=1; 39 for(int i=first[po];i;i=nxt[i]) 40 if(di+len[i]<dis[to[i]]) 41 { 42 dis[to[i]]=di+len[i]; 43 q.push((node){to[i],di+len[i]}); 44 } 45 //常规松弛(更新最短路径) 46 } 47 } 48 void add(int x,int y,int z) 49 { 50 tot++; 51 if(first[x]==0) 52 first[x]=tot; 53 else 54 nxt[last[x]]=tot; 55 last[x]=tot; 56 to[tot]=y; 57 len[tot]=z; 58 } 59 int main() 60 { 61 scanf("%d",&T); 62 for(int ii=1;ii<=T;++ii) 63 { 64 scanf("%d%d%d%d%d",&n,&k,&x,&m,&s); 65 n++; 66 for(int i=1;i<=n;++i) 67 first[i]=0; 68 for(int i=1;i<=m;++i) 69 { 70 int x,y,z; 71 scanf("%d%d%d",&x,&y,&z); 72 add(x,y,z); 73 add(y,x,z); 74 } 75 for(int i=1;i<=k;++i) 76 { 77 add(i,n,x); 78 add(n,i,0); 79 } 80 for(int i=1;i<=n;++i) 81 dis[i]=INF; 82 for(int i=1;i<=n;++i) 83 vis[i]=0; 84 dijistra(); 85 for(int i=1;i<n;++i) 86 printf("%lld ",dis[i]); 87 printf("\n"); 88 } 89 return 0; 90 }