最短路(模板)【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 }
相關文章
相關標籤/搜索