最短路算法模板

最短路算法模板

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

另外,图论(最短路问题只是图论的一小部分)中一般有如下约定:
•nnn 表示顶点数,mmm 表示边数
•V/v(vertex)V/v(vertex)V/v(vertex) 表示顶点,E/e(edge)E/e(edge)E/e(edge) 表示边
•uuu 表示起点(也叫源点),vvv 表示终点(也叫汇点),www 表示边权

最短路算法模板目录
•Dijkstra算法(包括朴素版和堆优化版)
•Bellman-Ford算法
•SPFA算法(Shortest Path Faster Algorithm)
•Floyd算法

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

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

朴素版

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

模板题AC代码:
#include
#include
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(mlogn)
空间复杂度:O(m)O(m)O(m)(因为用邻接表存图)

模板题AC代码1(邻接表的实现方式:链式前向星——用数组模拟单链表):
#include
#include
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

,greater

> 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(邻接表的实现方式:STLSTLSTL 里的 vectorvectorvector 容器):
#include
#include
#include
using namespace std;
const int M=2e4+5;
typedef pair<int,int> P;
vector

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

,greater

> 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(nm)O(nm)
空间复杂度:O(m)O(m)O(m)
适用场景(要求):图中可以存在负权边的单源最短路
•可以求不包含负环的单源最短路(包括不存在负权边、存在负权边但不存在负环)
•可以求只经过 kkk 条边的最短路(可以存在负环)
•可以判断图中是否存在负环

理解算法原理:
状态:
d[k][v]d[k][v]d[k][v] 的定义:从源点 uuu 出发,最多经过不构成负权值回路的 kkk 条边,到达汇点 vvv 的最短路径的长度
动态转移方程:
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!=vdist[k][v]=min(dist[k−1][v],dist[k−1][j]+map[j][v])),j=0,1,…,n−1;j!=v

模板题AC代码:
#include
#include
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−FordBellman-FordBellman−Ford 算法的队列 (动态)(动态)(动态) 优化
时间复杂度:O(km)O(km)O(km) ,kkk 为所有顶点进队的平均次数;期望是 O(m)O(m)O(m) 的,可以证明一般 k≤2k≤2k≤2 ;最坏 O(nm)O(nm)O(nm) ,kkk 近似等于 nnn 。
空间复杂度:O(m)O(m)O(m)(因为用邻接表存图)
适用场景(要求):图中可以存在负权边的单源最短路
•可以求不包含负环的单源最短路(包括不存在负权边、存在负权边但不存在负环)
•可以判断图中是否存在负环

模板题AC代码1(邻接表的实现方式:链式前向星——用数组模拟单链表):
#include
#include
#include
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 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(邻接表的实现方式:STLSTLSTL 里的 vectorvectorvector 容器):
#include
#include
#include
using namespace std;
const int M=2e4+5;
typedef pair<int,int> P;
vector

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 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(n3)O(n^3)O(n
3
)
空间复杂度:O(n2)O(n^2)O(n
2
)(因为用邻接矩阵存图)
适用场景(要求):图中可以存在负权边的多源汇最短路
•可以求不包含负环的多源汇最短路(包括不存在负权边、存在负权边但不存在负环)

理解算法原理:
状态:
d[k][i][j]d[k][i][j]d[k][i][j]的定义:只能使用第 111 号到第 kkk 号点作为中间媒介时,点 iii 到点 jjj 之间的最短路径长度
动态转移方程:
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])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
#include
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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值