最短路(模板)【CodeChef CLIQUED,洛谷P3371】

自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 }
相关文章
相关标签/搜索