图论报告
浅谈最短路
标签(空格分隔): 算法报告 图论 最短路
内容
- 最短路
- Floyd v3
- Bellman-ford v∗e
- Dijkstra v2
- Dijkstra Heap优化 vlogv+e∗logv
- SFPA v∗e
- A*
- 所有节点对最短路
- 矩阵乘法 v4
- Floyd-Warshall v3
- Johnson * (可于负权图) v2logv
- 最小生成树
- Prim v2 (邻接表 elogv )
- Kruskal eloge
- 具体题目
最短路大家族
Floyd ——正负权均可
这是一个耿直的,但是最容易写的代码。
本质是dp
对于只使用前 k 个点,求得最优解
那么就可以dp出 使用前 k + 1 个点的最优解
以下是dp过程
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];
Bellman-ford ——正负权均可
Bellman-ford是 不断松弛 寻得最优的过程
每次对所有边松弛操作一次
可以证明,至多 v-1 次,即可求得解
for(int i=1; i<=v-1; i++)
for(int j=1; j<=e; j++){
int u = edge[j].u;
int v = edge[j].v;
int weight = edge[j].weight;
if(dis[v] > dis[u] + weight)
dis[v] = dis[u] + weight;
}
// 判断是否存在负环
bool flag = 0;
for(int i = 1; i<=e; i++){
if(dis[edge[i].v] > dis[edge[i].u] + edge[i].weight){
flag = 1;
break;
}
}
// flag == 1 存在负环
Dijkstra —— 无法负权
Dij是每次通过获得最快能到达点,在以此继续贪心,来得到最短路的算法。
对于如下 n = 3 的邻接矩阵
0 | 2 | 3 |
---|---|---|
2 | 0 | -2 |
3 | -2 | 0 |
在更新的过程中 Dis[i]的状态如下
步骤 | dis1 | dis2 | dis[3] | 过程 |
---|---|---|---|---|
0 | 0 | ∞ | ∞ | 从点1触发 |
1 | 0 | 2 | 3 | 从点2触发 |
2 | 0 | 2 | 1 | 完毕 |
那么,dis2通过Dij得出的结果是 2
但是,很明显,可以通过 1->3->2的方式得到一个 1 的更优解
为什么Dij会失败呢?
Dij 是贪心,每次贪心得出(最快能到达的点)
负权会产生影响,把之前得到的某个点再次优化
但是这个被影响的点已经跑完了,就不会更新他所能影响的点
Dijkstra —— Heap 优化
Dij的算法步骤如下
- 在未纳入的点中,获得最优点①
- 以该点出发,更新其他点 ②
步骤① 会重复执行 v 次
步骤② 执行的总次数实际上所有的边 即 e 次
而步骤 ① 需要 获得最优点,朴素的搜索需要 v 次
步骤② 的更新朴素实现是
所以总时间是 v∗v+e∗1
Dij的Heap优化在于,把获得最优点和更新其他点用堆来实现
获得最优点即是删除堆顶 复杂度为 logV
更新其他点则是修改堆的节点值 复杂度为 logV
所以 Heap 优化后的Dij,时间为 v∗logv+e∗logv
即复杂度为 O(elogv)
实际上,当