图上最短路径算法分为单源最短路、多源最短路,单源常见算法:Bellman-ford、SPFA、Dijkstra及其堆优化;多源常见算法:Floyd。
单源最短路
Bellman-ford
记 \(dis_u\) 为源点到 \(u\) 当前的最短路。循环,每一次循环:枚举每一条边,尝试利用这一条边和边连接的两个节点中一个已经确定其最短路径的节点的 \(dis\) 来松弛另一个节点的 \(dis\)。一共经过 \(n-1\) 次这样的对所有边的松弛操作可以保证所有 \(dis\) 确定。(具有判断负权环的功能)的如果此时再循环一次发现还有边可以被松弛,那么说明,一定存在负权环。
时间复杂度:\(O(n\cdot m)\)
模板:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5,M=1e6,inf=1e9;
int dis[N],u[M],v[M],w[M];
int main()
{
int n,m,s;
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
cin>>u[i]>>v[i]>>w[i];
//u[m+i]=v[i],v[m+i]=u[i],w[m+i]=i;
}
for(int i=1;i<=n;i++) dis[i]=inf;
dis[s]=0;
for(int i=1;i<=n-1;i++)
for(int j=1;j<=m;j++)
if(dis[v[j]]>dis[u[j]]+w[j])
dis[v[j]]=dis[u[j]]+w[j];
bool flag=true;
for(int i=1;i<=m;i++)
if(dis[v[i]]>dis[u[i]]+w[i]){
flag=false;
break;
}
if(flag) for(int i=1;i<=n;i++) cout<<dis[i]<<endl;
else puts("-1");
return 0;
}
SPFA
一说 SPFA 是 Bellman-ford 的优化,一说可以理解成不同思想的两个独立算法。
首先将源点放入队列,当队列不为空时循环;每次循环取出队首记为 \(now\),把 \(now\) 的所有相连的节点遍历一遍,遍历到点 \(x\),判断 \(dis_x\) 是否大于 \(dis_{now}+e_{now,x}\),即松弛。减少了 Bellman-ford 无意义的尝试,复杂度 \(O(k\cdot m)\),其中 \(k\) 为常数且一般不超过 \(4\)。注意当出现负权环时队列永远不可能为空,即死循环。利用 Bellman-ford 判断负权环是否存在。
模板:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5,inf=1e9;
struct type {
int node,edge;
}t;
vector<type> G[N];
queue<int> Q;
int dis[N],inque[N];
int main()
{
int n,m,s,u,v,w;
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
t.node=v,t.edge=w,G[u].push_back(t);
//t.node=u,t.edge=w,G[v].push_back(t);
}
for(int i=1;i<=n;i++) dis[i]=inf;
Q.push(s);
dis[s]=0,inque[s]=1;
while(!Q.empty()){
int now=Q.front();
Q.pop(),inque[now]=0;
for(int i=0;i<G[now].size();i++)
if(dis[G[now][i].node]>dis[now]+G[now][i].edge){
dis[G[now][i].node]=dis[now]+G[now][i].edge;
Q.push(G[now][i].node);
inque[G[now][i].node]=1;
}
}
for(int i=1;i<=n;i++) cout<<dis[i]<<endl;
return 0;
}
Dijkstra 朴素
同样考虑松弛。记源点为 \(s\)。初始时 \(dis_s\) 已定,\(dis_i\) 设为 \(e_{s,i}\)。剩下的 \(n-1\) 个还要经过松弛判断是否确定的 \(dis\),所以进行 \(n-1\) 次循环。每次循环:从当前所有的 \(dis\) 中找到最小的一个 \(dis_x\),可知 \(dis_x\) 为定值;尝试对各点的 \(dis\) 用 \(x\) 松弛,即用 \(dis_x+e_{x,j}\) 尝试松弛 \(dis_j\)(\(j=1,2,\ldots,n\))。最后得到的 \(dis\) 数组中所有值皆确定。
时间复杂度:\(O(n^2)\)
模板:【略】
Dijkstra 堆优化
待更新……
多源最短路
Floyd
Floyd 可以求得各点之间的最短路。对于 \(e_{i,j}\) 考虑通过 \(k\) 来松弛,即如果 \(e_{i,j}>e_{i,k}+e_{k,j}\) 则松弛 \(e_{i,j}\)。\(k,i,j\) 分别从 \(1\sim n\) 循环,循环层序不能颠倒,复杂度 \(O(n^3)\)。
Floyd 可以判断一点是否可以到达另一点。如果原来就可以 \(i\) 到达 \(j\),或者通过 \(k\) 中转存在 \(i\to k\) 可达且 \(k\to j\) 可达那么更新 \(e_{i,j}\)。
模板(核心5行):
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(e[i][j]>e[i][k]+e[k][j]) //松弛,最短路
e[i][j]=e[i][k]+e[k][j];
/*
e[i][j]=e[i][j]|e[i][k]&e[k][j]; //判断是否i→j可达
*/
标签:node,松弛,int,短路,now,dis 来源: https://www.cnblogs.com/impyl/p/14955872.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。