SPFA (Bellman-Ford 的队列优化版本):
1、求单源最短路径,即每个点到源点的最短距离
2、可以处理负权边的情况
3、可以判断是否出现负权回路
#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
struct node{int x,y,t;
};
int n,m,dis[10001],count[10001];
vector<node> g[10001];
bool flag,inque[10001];
queue<int> q;
bool spfa(int S){memset(dis,0x7f,sizeof(dis));memset(inque,false,sizeof(inque));dis[S]=0;count[S]++;inque[S]=true;q.push(S);while(!q.empty()){int u=q.front();q.pop();inque[u]=false;for(int i=0;i<g[u].size();i++){int v=g[u][i].y;int w=g[u][i].t;if(dis[u]+w<dis[v]){dis[v]=dis[u]+w;if(inque[v]==false){q.push(v);count[v]++;inque[v]=true;if(count[v]==n){return false;}}}}}return true;
}
int main(){cin>>n>>m;for(int i=1;i<=m;i++){int x,y,v;cin>>x>>y>>v;g[x].push_back((node){x,y,v});}flag=spfa(1);if(flag==true){cout<<dis[n];}return 0;
}
时间复杂度:O(kM)~O(NM)
常见技巧:
-
在给平面直角坐标系构图,判断连通性时,仅需要将相邻的点连接(先按照x坐标或y坐标排序)
-
求一点到多点的距离,可以反向连边
-
有时会进行拆点
Floyed:
1、求多源最短路径,即点与点之间的最短距离
2、可以处理负权边的情况
3、不能出现负环
一个应用:最短回路(无向图)(有向图算d[s][s])
#include<iostream>
using namespace std;
int dist[101][101],cost[101][101],n,m,s,t,w,ans;
const int maxn=99999999;
int main(){while(cin>>n>>m){ans=maxn;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){dist[i][j]=maxn;cost[i][j]=-1;}}for(int i=1;i<=m;i++){cin>>s>>t>>w;dist[s][t]=dist[t][s]=w;cost[s][t]=cost[t][s]=w;}for(int k=1;k<=n;k++){for(int i=1;i<k;i++){for(int j=i+1;j<k;j++){if(cost[i][k]==-1||cost[k][j]==-1)continue;ans=min(ans,dist[i][j]+cost[i][k]+cost[k][j]);}}//省略这段循环则为floyed基本模版 for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);}}}if(ans!=maxn)cout<<ans<<endl;else cout<<"No solution."<<endl; }
}
时间复杂度:O(N^3)
Dijkstra:
1、求单源最短路径
2、不能处理负权
普通版:
void dijkstra() {memset(dis,127/3,sizeof(dis));//初始化 v[1]=1;dis[1]=0;for(int i=1;i<=n;++i){int k=0;for(int j=1;j<=n;++j) if(!v[j]&&(k==0||dis[j]<dis[k])) k=j;v[k]=1;for(int j=1;j<=n;++j) if(!v[j]&&dis[k]+a[k][j]<dis[j])dis[j]=dis[k]+a[k][j];}
}
时间复杂度:O(N^2)
堆优化版:
int dis[N],vis[N];
priority_queue<pr,vector<pr> ,greater<pr> > que;
void dijkstra(int s){memset(dis,0x3f,sizeof(dis));memset(vis,0,sizeof(vis));dis[s]=0;que.push(make_pair(0,s));while(!que.empty()){pr tmp=que.top();que.pop();int d=tmp.first;int u=tmp.second;//if(d!=dis[u]) continue;(等价于if(vis[u]) continue;)if(vis[u]) continue;vis[u]=1;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;int w=edge[i].w;if(dis[v]>dis[u]+w){dis[v]=dis[u]+w;que.push(pr(dis[v],v));}}}
}
其它优化:博客
时间复杂度:O((M+N)logM) (其中M为边数,N为点数)
update:2021/1/23
- 如何用单源最短路径算法求多源最短路径问题?(可以出发/终止的点有多个,与Floyed求解问题不同)
---- 建立一个超级起点、超级终点,将所有可能的起点、终点和超级起点、超级终点连接