最短路算法模板
以下模板全部依据 此模板题 给出,一定要理解模板,具体题目请具体分析,不要死搬硬套
另外,图论(最短路问题只是图论的一小部分)中一般有如下约定:
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;
}