最短路算法模板4种算法

6 篇文章 0 订阅
2 篇文章 0 订阅

最短路算法模板

以下模板全部依据 此模板题 给出,一定要理解模板,具体题目请具体分析,不要死搬硬套

另外,图论(最短路问题只是图论的一小部分)中一般有如下约定:

n 表示顶点数,m 表示边数
/v(vertex) 表示顶点)E/e(edge) 表示边
u 表示起点(也叫源点),v表示终点(也叫汇点),w 表示边权
最短路算法模板目录
Dijkstra算法(包括朴素版和堆优化版)
Bellman-Ford算法
SPFA算法(Shortest Path Faster Algorithm)
Floyd算法

Dijkstra算法(包括朴素版和堆优化版)

算法原理:贪心
适用场景(要求):图中不能存在负权边的单源最短路

只可以求不存在负权边的单源最短路
朴素版
时间复杂度:O(n^2)

空间复杂度:O(n^2)
(因为用邻接矩阵存图)

模板题AC代码:

#include <iostream>
#include <cstring>
using namespace std;
const int N=1e2+5;
int n,m,a,b,c;
bool st[N];
int dist[N],map[N][N];
int dijkstra() {
    memset(dist,0x3f,sizeof(dist));
    memset(st,0,sizeof(st));
    dist[1]=0;
    for(int i=1; i<n; i++) {
        int t=-1;
        for(int j=1; j<=n; j++) {
            if(!st[j]&&(t==-1||dist[j]<dist[t])) t=j;
        }
        if(t==n) return dist[t];
        st[t]=true;
        for(int j=1; j<=n; j++) dist[j]=min(dist[j],dist[t]+map[t][j]);
    }
    return dist[n];
}
int main() {
    while(cin>>n>>m&&(n||m)) {
        memset(map,0x3f,sizeof(map));
        while(m--) {
            cin>>a>>b>>c;
            map[a][b]=map[b][a]=c;
        }
        cout<<dijkstra()<<endl;
    }
    return 0;
}

堆优化版
时间复杂度:O(mlogn)O(mlogn)
空间复杂度:O(m)O(m)(因为用邻接表存图)

模板题AC代码1

(邻接表的实现方式:链式前向星——用数组模拟单链表):

#include <iostream>
#include <queue>
using namespace std;
const int M=2e4+5;
typedef pair<int,int> P;
int n,m,a,b,c;
bool st[M];
int dist[M];
int h[M],v[M],nv[M],e[M],id;
void add(int from,int to,int w) {
	v[id]=to,e[id]=w,nv[id]=h[from],h[from]=id++;
}
int dijkstra() {
	fill(dist,dist+M,0x3f3f3f3f);
	fill(st,st+M,0);
	dist[1]=0;
	priority_queue<P,vector<P>,greater<P> > heap;
	heap.push({0,1});
	while(heap.size()) {
		P p=heap.top();
		heap.pop();
		int distance=p.first,ver=p.second;
		if(ver==n) return distance;
		if(st[ver]) continue;
		st[ver]=true;
		for(int i=h[ver]; i!=-1; i=nv[i]) {
			int j=v[i];
			if(dist[j]>distance+e[i]) {
				dist[j]=distance+e[i];
				heap.push({dist[j],j});
			}
		}
	}
	return dist[n];
}
int main() {
	while(cin>>n>>m&&(n||m)) {
		fill(h,h+M,-1);
		id=0;
		while(m--) {
			cin>>a>>b>>c;
			add(a,b,c),add(b,a,c);
		}
		cout<<dijkstra()<<endl;
	}
	return 0;
}

## 模板题AC代码2

(邻接表的实现方式:STLSTL 里的 vectorvector 容器):

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int M=2e4+5;
typedef pair<int,int> P;
vector<P> e[M];
int n,m,a,b,c;
bool st[M];
int dist[M];
void add(int from,int to,int w) {
    e[from].push_back({to,w});
}
int dijkstra() {
    memset(dist,0x3f,sizeof(dist));
    memset(st,0,sizeof(st));
    dist[1]=0;
    priority_queue<P,vector<P>,greater<P>> heap;
    heap.push({0,1});
    while(heap.size()) {
        P p=heap.top();
        heap.pop();
        int distance=p.first,ver=p.second;
        if(ver==n) return distance;
        if(st[ver]) continue;
        st[ver]=true;
        for(auto t:e[ver]) {
            int to=t.first,w=t.second;
            if(dist[to]>distance+w) {
                dist[to]=distance+w;
                heap.push({dist[to],to});
            }
        }
    }
    return dist[n];
}
int main() {
    while(cin>>n>>m&&(n||m)) {
        for(int i=1; i<=n; i++) e[i].clear();
        while(m--) {
            cin>>a>>b>>c;
            add(a,b,c),add(b,a,c);
        }
        cout<<dijkstra()<<endl;
    }
    return 0;
}

Bellman-Ford算法

算法原理:动态规划
时间复杂度:O(nm)
空间复杂度:O(m)
适用场景(要求):图中可以存在负权边的单源最短路

可以求不包含负环的单源最短路(包括不存在负权边、存在负权边但不存在负环)
可以求只经过 k条边的最短路(可以存在负环)
可以判断图中是否存在负环
理解算法原理:
状态:
d[k][v]的定义:从源点 uu 出发,最多经过不构成负权值回路的 k 条边,到达汇点 v 的最短路径的长度
动态转移方程:
dist[k][v]=min(dist[k−1][v],dist[k−1][j]+map[j][v])),j=0,1, . . . ,n−1;j!=vdist[k][v]=min(dist[k−1][v],dist[k−1][j]+map[j][v])),j=0,1,…,n−1;j!=v

模板题AC代码:

#include <iostream>
#include <cstring>
using namespace std;
const int M=2e4+5;
int n,m,a,b,c,id;
int dist[M],pre[M];
struct E {
	int from,to,w;
} e[M];
int Bellman_Ford() {
	memset(dist,0x3f,sizeof(dist));
	dist[1]=0;
	for(int i=1; i<n; i++) {
		memcpy(pre,dist,sizeof(dist));
		for(int j=0; j<id; j++) {
			int from=e[j].from,to=e[j].to,w=e[j].w;
			dist[to]=min(dist[to],pre[from]+w);
		}
	}
	return dist[n];
}
int main() {
	while(cin>>n>>m&&(n||m)) {
		id=0;
		while(m--) {
			cin>>a>>b>>c;
			e[id++]= {a,b,c};
			e[id++]= {b,a,c};
		}
		cout<<Bellman_Ford()<<endl;
	}
	return 0;
}

SPFA算法(Shortest Path Faster Algorithm)

算法原理:对 Bellman-Ford 算法的队列 (动态)(动态) 优化
时间复杂度:O(km),k为所有顶点进队的平均次数;期望是 O(m)的,可以证明一般 k≤2;最坏 O(nm) ,k 近似等于 n 。
空间复杂度:O(m)(因为用邻接表存图)
适用场景(要求):图中可以存在负权边的单源最短路

可以求不包含负环的单源最短路(包括不存在负权边、存在负权边但不存在负环)
可以判断图中是否存在负环

模板题AC代码1

(邻接表的实现方式:链式前向星——用数组模拟单链表):

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int M=2e4+5;
int n,m,a,b,c;
bool st[M];
int dist[M];
int h[M],v[M],nv[M],e[M],id;
void add(int from,int to,int w) {
    v[id]=to,e[id]=w,nv[id]=h[from],h[from]=id++;
}
int SPFA() {
    memset(dist,0x3f,sizeof(dist));
    memset(st,0,sizeof(st));
    dist[1]=0;
    queue<int> q;
    q.push(1);
    while(q.size()) {
        int t=q.front();
        q.pop();
        st[t]=false;
        for(int i=h[t]; i!=-1; i=nv[i]) {
            int j=v[i];
            if(dist[j]>dist[t]+e[i]) {
                dist[j]=dist[t]+e[i];
                if(!st[j]) {
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
    return dist[n];
}
int main() {
    while(cin>>n>>m&&(n||m)) {
        memset(h,-1,sizeof(h));
        id=0;
        while(m--) {
            cin>>a>>b>>c;
            add(a,b,c),add(b,a,c);
        }
        cout<<SPFA()<<endl;
    }
    return 0;
}

模板题AC代码2

(邻接表的实现方式:STLSTL 里的 vector 容器):

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int M=2e4+5;
typedef pair<int,int> P;
vector<P> e[M];
int n,m,a,b,c;
bool st[M];
int dist[M];
void add(int from,int to,int w) {
    e[from].push_back({to,w});
}
int SPFA() {
    memset(dist,0x3f,sizeof(dist));
    memset(st,0,sizeof(st));
    dist[1]=0;
    queue<int> q;
    q.push(1);
    while(q.size()) {
        int t=q.front();
        q.pop();
        st[t]=false;
        for(auto x:e[t]) {
            int to=x.first,w=x.second;
            if(dist[to]>dist[t]+w) {
                dist[to]=dist[t]+w;
                if(!st[to]) {
                    q.push(to);
                    st[to]=true;
                }
            }
        }
    }
    return dist[n];
}
int main() {
    while(cin>>n>>m&&(n||m)) {
        for(int i=1; i<=n; i++) e[i].clear();
        while(m--) {
            cin>>a>>b>>c;
            add(a,b,c),add(b,a,c);
        }
        cout<<SPFA()<<endl;
    }
    return 0;
}

Floyd算法

算法原理:动态规划
时间复杂度:O(n^3
空间复杂度:O(n^2)
(因为用邻接矩阵存图)
适用场景(要求):图中可以存在负权边的多源汇最短路

可以求不包含负环的多源汇最短路(包括不存在负权边、存在负权边但不存在负环)
理解算法原理:
状态:
d[k][i][j]的定义:只能使用第 11 号到第 kk 号点作为中间媒介时,点 i 到点 j 之间的最短路径长度
动态转移方程:
d[k][i][j]=min(d[k−1][i][j],d[k−1][i][k]+d[k−1][k][j])(k,i,j∈[1,n])d[k][i][j]=min(d[k−1][i][j],d[k−1][i][k]+d[k−1][k][j])(k,i,j∈[1,n])

模板题AC代码:

#include <iostream>
#include <cstring>
using namespace std;
const int N=1e2+5;
int n,m,a,b,c;
int map[N][N];
int Floyd() {
    for(int k=1; k<=n; k++)
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
    return map[1][n];
}
int main() {
    while(cin>>n>>m&&(n||m)) {
        memset(map,0x3f,sizeof(map));
        for(int i=1; i<=n; i++) map[i][i]=0;
        while(m--) {
            cin>>a>>b>>c;
            map[a][b]=map[b][a]=c;
        }
        cout<<Floyd()<<endl;
    }
    return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值