最短路

寫在前面的話:寫寫複習下它,過久沒怎麼寫這類題了ios

文章部份內容出自《算法競賽進階指南》算法

單源最短路徑

這種問題就是說給一張有向圖,以某一個節點(通常爲1號節點),記錄下其餘每個點數組

到達這個1號節點的最短路徑的長度。網絡

經常使用算法:Dijkstra,Bellman-Ford,SPFA(本質上是Bellman-Ford)閉包

Dijkstra算法

基本思路:

它是基於貪心算法的一種方案,只能用在全部邊長度非負的圖。由於若z爲負,全局最小值不可能被其餘點優化

更新了。其中,第一步選出來的x已經知足--dist[x]是起點到x點的最短路徑。本質上,咱們在不斷尋找全局spa

最小值在進行標記和拓展,最後就獲得起點1到每一個節點的最短路。code

過程:

1.初始化dist[i]數組,即原點到i點的距離,dist[1]=0,其餘點初始化到原點距離爲+∞;blog

2.找出一個未被標記的且dist[x]最小的點x,再標記節點x;隊列

3.掃描x的全部出邊,若dist[y]>dist[x]+z,就更新dist[y] = dist[x]+z;

4.重複2--3,直到全部點被標記爲止

固然咱們能夠發現,在找全局最小值的時候,代碼能夠繼續優化,即找這個最小值能夠用二叉堆去找,

複雜度從O($n^{2}$) 進化成 O($m log n$)。

#include<iostream> #include<queue> #include<cstring> #include<cstdio> using namespace std; const long MAXN=1000010; long n,m; long head[MAXN],Next[MAXN],ver[MAXN],edge[MAXN],tot=0; priority_queue< pair<long,long> > q; inline void add(long x,long y,long z){ ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot; } bool v[MAXN]; long dist[MAXN]; inline void dijkstra(){ for(long i=1;i<=n;i++) dist[i]=999999; memset(v,0,sizeof v); dist[1]=0; q.push(make_pair(0,1)); while(q.size()){ long x=q.top().second; q.pop(); if(v[x]) continue; v[x]=1; for(long i=head[x];i;i=Next[i]) { long y=ver[i],z=edge[i]; if(dist[y]>dist[x]+z){ dist[y]=dist[x]+z; q.push(make_pair(-dist[y],y)); } } } } int main(){ scanf("%ld%ld",&n,&m); long x,y,z; for(long i=1;i<=m;i++){ scanf("%ld%ld%ld",&x,&y,&z); add(x,y,z); } dijkstra(); for(long i=1;i<=n;i++) printf("%ld\n",dist[i]); }

Bellman-Ford算法

基本思路

它是基於迭代的思想去考慮,即便全部邊(x,y,z)知足三角形不等式:d[y]<=d[x]+z

以下圖:

 

過程:

1.掃描全部邊,若dist[y]>dist[x]+z,就使dist[y]=dist[x]+z;

2.重複上述步驟直到沒有更新發生爲止。

複雜度高達O(nm),但它優於dijkstra的一點,是它的邊權能夠爲負值。

SPFA算法

基本思路:

spfa在國際上通稱」隊列優化的Bellman-Ford算法「,它用隊列保存了待擴展的節點,每一次的入隊

至關於完成一次更新,使得節點慢慢收斂,即鬆弛,稀疏圖效率高。

由於對於一個從x到y的邊,節點x尚未鬆弛過,那y就不必鬆弛,因此咱們用隊列記錄鬆弛過的點,

避免了bellman-ford中的冗餘鬆弛操做。

過程:

1.創建一個隊列,初始入隊節點1;

2.取出隊頭,並掃描全部出邊,若dist[y]>dist[x]+z,則更新dist[y]=dist[x]+z,並判斷y是否在隊列中,若

不在,則將y入隊;

3.重複上述步驟直至隊列爲空。

用簡略語言概述就是,咱們有一個鬆弛點x,嘗試x能到達的點y,若y能夠被鬆弛,就更新dist[y],在

這個條件下,若y還沒入隊,那就把這個鬆馳過的點入隊,直到隊列爲空。

#include<iostream> #include<queue> #include<cstring> #include<cstdio> using namespace std; const long MAXN=1000010; long n,m; long head[MAXN],Next[MAXN],ver[MAXN],edge[MAXN],tot=0; queue <long> q; inline void add(long x,long y,long z){ ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot; } bool v[MAXN]; long dist[MAXN]; inline void spfa(){ for(long i=1;i<=n;i++) dist[i]=999999; memset(v,0,sizeof v); dist[1]=0; q.push(1); v[1]=1; while(q.size()){ long x=q.front(); q.pop(); v[x]=0; for(long i=head[x];i;i=Next[i]) { long y=ver[i],z=edge[i]; if(dist[y]>dist[x]+z){ dist[y]=dist[x]+z; if(v[y]==0) q.push(y),v[y]=1; } } } } int main(){ scanf("%ld%ld",&n,&m); long x,y,z; for(long i=1;i<=m;i++){ scanf("%ld%ld%ld",&x,&y,&z); add(x,y,z); } spfa(); for(long i=1;i<=n;i++) printf("%ld\n",dist[i]); }

特別地,當咱們要去斷定負環的時候,咱們能夠用一個數組c[i]表示1到i的最短路徑上點的個數,每一次鬆弛x-y時,

咱們就用c[y]=c[x]+1,當c[y]>n時,即存在負環。

注:

其實對比dijkstra和spfa,咱們能夠發現,dijkstra針對每個最小距離點進行擴展,

spfa則是經過迭代思想將全部邊進行擴展,天然spfa的複雜度會高於dijkstra,可是spfa同時也具備

能夠更加容易加入各類其餘算法和對負權的邊,負環的處理。

舒適提示,面對一個不存在負權邊的圖最好用dijkstra,由於spfa易被惡意卡掉。

任意兩點間的最短路徑

Floyd算法

基本思路:

它是一種基於動態規劃的算法,咱們能夠用D[k,i,j]來表示i通過編號不超過k的節點後到達j的最短路,因此

咱們能夠將其劃分爲兩個子問題,一是congi通過編號不超過k-1的節點到j,或者從i通過k節點到達j。

則 D[k,i,j]=min(D[k-1,i,j],D[k-1,i,k]+D[k-1,k,j])

因此k是階段,應該放在最外層

咱們也能夠用D保存鄰接矩陣,省略k這一維,即:

D[i,j]=min(D[i,j],D[i,k]+D[k,j])

也能夠理解爲從i到j,看看目前的狀況好,仍是再通過一箇中間點k更好。

過程:

這就再也不贅述了,太過簡單......

 

#include<iostream> #include<cmath> using namespace std; const long MAXN=10010; long dist[MAXN][MAXN]; long n,m; int main(){ cin>>n>>m; for(long i=1;i<=n;i++) for(long j=1;j<=n;j++) dist[i][j]=999999; for(long i=1;i<=n;i++) d[i][i]=0; long x,y,z; for(long i=1;i<=m;i++){ cin>>x>>y>>z; if(dist[x][y]>z) dist[x][y]=z; } for(long k=1;k<=n;k++) for(long i=1;i<=n;i++) for(long j=1;j<=n;j++) dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]); for(long i=1;i<=n;i++){ for(long j=1;j<=n;j++) cout<<dist[i][j]<<" "; cout<<endl; } }

範圍,數值最大值能夠自行更改

傳遞閉包

在交際網絡中存在若干元素和若干對二元關係,關係具備傳遞性,請推導出儘可能多的元素間的關係----傳遞閉包

易得,咱們能夠用floyd解決此類問題

#include<iostream> #include<cmath> using namespace std; const long MAXN=10010; bool dist[MAXN][MAXN]; long n,m; int main(){ cin>>n>>m; for(long i=1;i<=n;i++) dist[i][i]=1; long x,y,z; for(long i=1;i<=m;i++){ cin>>x>>y; dist[x][y]=dist[y][x]=1; } for(long k=1;k<=n;k++) for(long i=1;i<=n;i++) for(long j=1;j<=n;j++) dist[i][j]|=dist[i][k]&dist[k][j]; }
相關文章
相關標籤/搜索