一些基本的最短路算法,记下来代码,用到的时候可以直接改成函数套用。
Floyd - Warshall ,时间复杂度 O(n^3)
// Floyed
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxLen = 105;
int e[maxLen][maxLen];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
// init()
for(int i=1;i<=n;i++) {
dis
for(int j=1;j<=n;j++) {
if(i == j) e[i][j] = 0;
else e[i][j] = inf;
}
}
// Floyed
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中
}
Floyed算法时间复杂度较高,一般仅用于求多源最短路径,可以处理带负权边的图,判断方法:跑两此Floyed,如果第二次仍能缩减某条边,则含有负边权。
Dijkstra优先队列(堆)优化,时间复杂度 O( (M+N)log N)
// Floyed
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxLen = 105;
struct Edge {
int to,w;
Edge(int too=-1,int ww=inf) {
to = too;
w = ww;
}
friend bool operator < (const Edge e1,const Edge e2) {
return e1.w > e2.w;
}
};
vector< Edge> v[maxLen];
int dis[maxLen];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
//init
memset(dis,inf,sizeof dis);
dis[1] = 0; //以1为源点
// vector.clear();
//建立邻接表
int st,ed,w;
for(int i=1;i<=m;i++) {
scanf("%d%d%d",&st,&ed,&w);
v[st].push_back(Edge(ed,w));
v[ed].push_back(Edge(st,w));
}
//Dijkstra
priority_queue< Edge> q;
q.push(Edge(1,0));
while(!q.empty()) {
int u = q.top().to;
for(int i=0;i<v[u].size();i++) {
int t = v[u][i].to;
if(dis[t] > dis[u]+v[u][i].w) {
dis[t] = dis[u]+v[u][i].w;
q.push(Edge(t,dis[t]));
}
}
q.pop();
}
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
return 0;
}
算法思想:用已经确定最短长度的路径去缩减未确定的路径,直到所有路径都确定最短(即不能再继续缩减)
Dijkstra,用于求单源最短路径,不能处理带负权边的图
Bellman - Ford,时间复杂度O(NM)
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxLen = 105;
struct Edge {
int u,v,w;
}e[maxLen];
int dis[maxLen];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
memset(dis,inf,sizeof dis);
dis[1] = 0; //以1为源点
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
//bellman
bool flag = true;
for(int i=1;i<=n-1 && flag;i++) {
flag = false;
for(int j=1;j<=m;j++) {
if(dis[e[j].v] > dis[e[j].u]+e[j].w) {
dis[e[j].v] = dis[e[j].u]+e[j].w;
flag = true;
}
}
if(!flag) break;
}
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
printf("\n");
for(int j=1;j<=m;j++) { //判断是否含有负权边
if(dis[e[j].v] > dis[e[j].u]+e[j].w) {
dis[e[j].v] = dis[e[j].u]+e[j].w;
printf("含有负权边\n");
}
}
return 0;
}
Bellman - Ford用于求单源最短路,可以处理带负边权的图,
Bellman实际上是通过边缩减,有固定的缩减次数上限,而Dijkstra实际上是通过点缩减,如果有负权环,则会无限制的进行while队列循环。
SPFA,时间复杂度最坏: O(MN)
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxLen = 105;
struct Edge {
int to,w;
Edge(int too,int ww) {
to = too;
w = ww;
}
};
vector< Edge>v[maxLen];
int dis[maxLen],num[maxLen];
bool vis[maxLen];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
bool flag = false;
memset(dis,inf,sizeof dis);
memset(num,0,sizeof num);
memset(vis,false,sizeof vis);
dis[1] = 0; //以1为源点
int st,ed,w;
for(int i=1;i<=m;i++) {
scanf("%d%d%d",&st,&ed,&w);
v[st].push_back(Edge(ed,w));
v[ed].push_back(Edge(st,w));
}
queue< int> q;
q.push(1);
vis[1] = true;
while(!q.empty()) {
int k = q.front();
num[k]++;
if(num[k] > n) {
printf("含有负权回路\n");
flag = true;
break;
}
for(int i=0;i<v[k].size();i++) {
Edge t = v[k][i];
if(dis[t.to] > dis[k]+t.w) {
dis[t.to] = dis[k]+t.w;
if(!vis[t.to]) {
q.push(t.to);
vis[t.to] = true;
}
}
}
vis[k] = false;
q.pop();
}
if(!flag) {
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
}
return 0;
}
SPFA用于求单源最短路,可以处理带负权边的图
SPFA实际上就是Bellman - ford的队列优化 。与Dijkstra在写法上很相似,区别在于保证同一个时刻队列中不会出现两个相同的点。
习题:
POJ - 1806 Currency Exchange https://blog.csdn.net/weixin_42765557/article/details/98470352