图论——最短路基础(二)与Floyd

Part1:最短路基础

  对于最短路问题,常见的有5种算法:朴素Dijkstra,堆优化Dijkstra,Bellman_Ford,SPFA,Floyd。

  注:堆优化Dijkstra虽然有这样的名字,但它和朴素Dijkstra的差别还是蛮大的,或者可以说,堆优化Dijkstra是基于朴素Dijkstra的思想的一种新的算法。

  首先分析五种算法的时间复杂度,我们称节点数为n,边数为m:

    朴素Dijkstra:双层循环枚举,每一层枚举节点,所以时间复杂度为O(n^2)(不考虑常数,考虑常数的话不到n^2),朴素Dijkstra详解见这个

    堆优化Dijkstra:枚举每条边,对于每一条边,枚举与它相连的节点,并且查找点是基于一颗二叉树(堆),所以时间复杂度为O(m*log2n)。

  注:如果是稠密图,用朴素Dijkstra,如果是稀疏图,用堆优化Dijkstra,这是很有必要的,之后单独讲堆优化Dijkstra的时候会强调。

    Bellman_Ford:枚举每个点,对于每个点,枚举所有到 i 的边,所以时间复杂度为O(mn)。这适合于稀疏图。

    SPFA:枚举边,查找点,对于稠密图一般情况下时间复杂度为O(m),对于稠密图一般情况下时间复杂度为O(nm)。这适合于稠密图。

    Floyd:三层循环枚举,每一层枚举节点,时间复杂度为O(n^3)。

  对于无负权值的情况下,一般用朴素Dijkstra、堆优化Dijkstra、Floyd;

  对于有负权值的情况下,一般用Bellman_Ford、SPFA。

Part2:Floyd

  通过上面的时间复杂度分析,我们发现Floyd是最慢的,那还要他干嘛?

  正所谓存在即合理,Floyd用来解决最短路的一个分支——多源汇最短路。

  那什么是多源汇最短路呢?

  听名字听高大上,其实就是从多个起点到某个点的最短路,Floyd的时间复杂度不一定是O(n^3),而是O(K*n^2),这里的 K 表示起点的个数,当K=n时,时间复杂度自然就是O(n^3)了。

  前面分析时我还卖了关个子呢,就是这三层都枚举了什么。

    第一层枚举了起点,第二层和第三层和朴素Dijkstra的循环类似,都是枚举节点。之所以它慢,就是因为它多了一层枚举起点。

  它的代码极其简洁,正常存图后,在循环枚举内进行更改 g[i][j]=min(g[i][j],g[i][k]+g[k][j]);(这里用的是邻接矩阵,如果是邻接表,可以再单独开一个g[][]数组存储即可)。

  模板代码(作为一位压行大师,18行完成):

#include<bits/stdc++.h>
using namespace std;
int n,m,q,g[202][202],a,b,w;
int main() {
  memset(g,0x3f,sizeof g);
  for(int i=1; i<=n; i++) g[i][i]=0;
  cin>>n>>m>>q;
  while(m--) {
    cin>>a>>b>>w;
    g[a][b]=w;
  }
  for(int k=1; k<=n; k++) for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
  while(q--) {
    cin>>a>>b;
    cout<<g[a][b]<<endl;
  }
  return 0;
}

posted @ 2022-07-17 18:32  唯私の超电磁砲  阅读(47)  评论(0编辑  收藏  举报