两个图论最短路算法板子
$SPFA$(优化$Bellman-Ford$)
至于$Bellman-Ford$这里就不提了,毕竟已经淘汰了(复杂度满了$O(nm)$)
算法
大致思路:利用队列$bfs$优化$Bellman-Ford$,从当前点(第一次就是起点)开始,遍历每一个出边,更新到这条边终点的最短路径(如果可以更新的话),如果这个点未标记过,则把这个终点push进队列,并标记(表明更新过),之后每次从队首取出元素并将其置为未标记(目的是可重复更新),对当前点重复上述步骤即可得出答案
核心点:
- 每次取出队首元素后一定要去标记
- “可重复更新”是该算法的核心
- 复杂度为$O(ke)$,$k$是个常数(通常等于2左右),然而会有毒瘤题卡$SPFA$,使其复杂度达到满的$O(nm)$,因而在不含负权图中尽量使用$Dijkstra$
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=2147483647;
struct Edge{ int u,v,w,next; }e[1000005];
int n,m,s,ans,cnt,vis[1000005],head[1000005]; inline void addedge(int u,int v,int w){ e[++cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; } int dis[1000005]; inline void SPFA(){ for(int i=1;i<=n;i++){ dis[i]=inf; } queue<int> q; q.push(s); dis[s]=0; vis[s]=1; while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];i;i=e[i].next){ int v=e[i].v,w=d[i].w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; if(vis[v]==0){ vis[v]=1; q.push(v); } } } } }
int main(){ cin>>n>>m>>s; for(int i=1;i<=m;i++){ int u,v,w; cin>>u>>v>>w; addedge(u,v,w); } SPFA(); for(int i=1;i<=n;i++){ cout<<dis[i]<<" "; } return 0; }
|
$Dijkstra$
算法
大致思路:同$SPFA$的操作,但不同的是:思路类似于$Prim$算法,并不用重复更新某一个点,而是采用每次更新答案之前通过一顿循环,把当前情况下的到起点的最短路所对应的节点取出来,对这个节点的所有出边遍历并更新答案,然后重复上述过程,直到所有点都完成更新,复杂度是$O(n^2)$,显然不够给力,因此通常我们会采取堆优化
堆优化:由于每次需要去除当前情况下的最短路对应的节点,我们可以利用小根堆的特点,每次取出最小元素所需复杂度为$O(logn)$,因此算法的复杂度可以优化为$O((n+m)logn)$
核心点:
- 每次更新答案前取出当前情况下最短路径所对应的的节点,以此更新
- 大部分情况下不会被卡,十分好用
代码(堆优化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf = 2147483647; struct Edge{ int u,v,w,next; }e[1000005];
struct node{ int u,dis; bool operator <(const node&rhs)const{ return dis>rhs.dis; } };
int n,m,s,ans,cnt,now,head[1000005],dis[1000005]; inline void addedge(int u,int v,int w){ e[++cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt; }
inline void dijkstra(){ for(int i=1;i<=n;i++) dis[i]=inf; priority_queue<node> q; dis[s]=0; q.push((node){s,0}); while(!q.empty()){ node q1=q.top(); q.pop(); int u=q1.u; if(q1.dis>dis[u]) continue; for(int i=head[u];i;i=e[i].next){ int v=e[i].v,w=e[i].w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; q.push((node){v,dis[v]}); } } } }
int main(){ cin>>n>>m>>s; for(int i=1;i<=m;i++){ int u,v,w; cin>>u>>v>>w; addedge(u,v,w); } dijkstra(); for(int i=1;i<=n;i++){ cout<<dis[i]<<" "; } return 0; }
|