最短路系列链接
最短路总结1 最短路问题概述与朴素dijkstra
最短路总结2 堆优化dijkstra
最短路总结3 BellmanFord
最短路总结4 SPFA及应用
最短路总结5 floyd
1.最短路问题概述
若网络中的每条边都有一个数值(长度、成本、时间等),则找出两节点(通常是源节点和阱节点)之间总权和最小的路径就是最短路问题。
最短路问题,其实就是给定一个网络,给定一个或一组起点(术语叫源点),对这些源点给定其他点,询问这些点到源点的最短路径。
2. 分类
Notes:
-
单源是指单一起点到其他点的最短路,多源是指求每组起点到终点的最短路。
-
堆优化dijkstra是在朴素版基础上,利用小根堆的自动维持顺序的特性优化朴素版中的某一步。
-
SPFA算法一般不论稠密图还是稀疏图,都用邻接矩阵存储图。
-
SPFA算法本质上是利用宽搜的思想对 bellerman-ford算法做的优化,因此SPFA很强大,在绝大多数情况下可以用SPFA算法代替bellman-ford算法解决问题,因此重点掌握。
(其实也可以代替dijkstra) -
SPFA算法的局限:当图中出现一下两点之一时SPFA算法失效,只能使用bellman-ford算法。
(1)限制最短路的路径经过不超过k条边
(2)图中存在负权回路(回路边权之和为负数) -
SPFA算法可以用来判断是否存在负环,另:判断正环用tanjar算法
3.朴素dijkstra算法(On^2+m)
- 初始化各点到源点的距离
- 迭代n-1次(除源点外有n-1个点)
(1)在最短路未确定的点中,找距离源点最近的点v。(用bool数组标识该点的最短路是否已经确定)
(2)用v点去更新全图所有点的当前最短路距离(dist[])
(3)若i点满足:dist[i] > dist[v] + w[v][i]
则更新i点的dist值
更新全图后将该点设置为最短路已经确定
这里假设找到的v点是2点,用2点去更新全图,实际上这里就是更新3点,也就相当于判断这两条到路径谁更短:(1)1->2->3距离为dist[2]+w[2][3]
(2)1->3距离为dist[3]。推广到更大更复杂的网络中,也是一个个类似这样去比较更新,比较完所有点,则v点不可能再被更新,所以设置v点的最短路已经确定。
4.模板
int g[N][N]; // 存储每条边
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < n - 1; i ++ )
{
int t = -1; // 在还未确定最短路的点中,寻找距离最小的点
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
// 用t更新其他点的距离
for (int j = 1; j <= n; j ++ )
dist[j] = min(dist[j], dist[t] + g[t][j]);
st[t] = true;
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
5.板子题
给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1。
输入格式
第一行包含整数n和m。
接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。
输出格式
输出一个整数,表示1号点到n号点的最短距离。
如果路径不存在,则输出-1。
数据范围
1≤n≤5001≤n≤500,
1≤m≤1051≤m≤105,
图中涉及边长均不超过10000。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=550,inf=0x3f3f3f3f;
int n,m;
int g[N][N];
int dist[N];
bool st[N];
int dijkstra() {
//1.初始化
memset(dist,inf,sizeof(dist));
dist[1]=0;
//2.迭代n-1次
for(int i=0;i<n;i++) {
//3.在未确定最短路的点中找到距离源点最近的点
int t=-1;
for(int i=1;i<=n;i++)
if(!st[i] && (t==-1 || dist[i]<dist[t]))
t=i;
//4.用找到的点去更新全图
for(int i=1;i<=n;i++) {
dist[i]=min(dist[i],dist[t]+g[t][i]);
}
st[t]=1;
}
if(dist[n]>=1000000000) return -1;
else return dist[n];
}
int main()
{
cin >> n >> m;
memset(g,inf,sizeof(g));
while(m --) {
int x,y,z;
cin >> x >> y >> z;
if(x!=y) {
g[x][y]=min(g[x][y],z);
}
}
int t=dijkstra();
cout<<t;
return 0;
}
6.小细节
- 去重边:在存储图时,取两点之间多条边的最短的那条
- 去自环:在存储图时,自己到自己的边应当去除
- 无向边:区别只在于存储图的时候,存两边,一去一回。
int x,y,z;
cin >> x >> y >> z;
if(x!=y) {
g[x][y]=min(g[x][y],z);
}