求最短路径基本(常用)算法总结:

最短路径

  1. 朴素dijkstra

    • 思路:

      第一步先初始化dist[1] = 0,dist[i] = INF (dist数组表示某一点到起点的距离);

      第二步第一层循环for循环 1 - n遍历所有点,然后第二层循环for 1 - n,找到 st[t] 值为 false 且距离起点最近的点的下标值赋给变量 t 并且把st[t]的值设为true( st 数组是标记这个点的最短路径是否已经找到,找到则为true);

      第三步用 t 更新其它所有点的距离。

    • 例题:

    • 代码实现:

    #include <bits/stdc++.h>
    ​
    using namespace std;
    ​
    const int N = 510;
    int n,m;
    int g[N][N]; //存储每一条边的距离(邻接矩阵适合稠密图)
    int dist[N]; //存储每一个点到起点的距离
    bool st[N]; // 标记某一点的最短路径是否已经确定下来
    ​
    int dijkstra()
    {
        memset(dist, 0x3f, sizeof dist);
        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[t] > dist[j]))
                    t = j;
            }
            st[t] = true;
            for(int j = 1; j <= n; j ++ )
            {
                dist[j] = min(dist[j], dist[t] + g[t][j]);
            }
        }
        if(dist[n] == 0x3f3f3f3f) return -1;
        return dist[n];
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        memset(g, 0x3f, sizeof g);
        for(int i = 1; i <= m; i ++ )
        {
            int a,b,w;
            scanf("%d%d%d", &a, &b, &w);
            g[a][b] = min(g[a][b], w);
        }
        int res = dijkstra();
        printf("%d",res);
        return 0;
    }

  2. 堆优化版dijkstra:

    • 优化了找最小值所需的时间;

    • 例题:

    • 代码实现:

      #include <bits/stdc++.h>
      
      using namespace std;
      
      typedef pair<int,int> PII;//<距离起点的距离,节点编号>
      const int N = 150010;
      
      int n,m,idx;//当前指到哪个邻接点
      int h[N],w[N],e[N],ne[N];//e数组是存储
      int dist[N];
      bool st[N];
      
      //在a节点之后插入一个b节点,权重为c
      void add(int a,int b,int c)
      {
          e[idx] = b;
          ne[idx] = h[a];
          w[idx] = c;
          h[a] = idx ++ ;
      }
      
      int dijkstra()
      {
          memset(dist, 0x3f, sizeof dist);//所有距离初始化为无穷大
          dist[1] = 0;//1号节点距离起点为0
          priority_queue<PII, vector<PII>, greater<PII>> heap;//定义小根堆
          heap.push({0,1});//将1号节点插入堆
          while(heap.size())
          {
              auto t = heap.top();//取出堆顶顶点,因为是小根堆,所以堆顶就是最小值
              heap.pop();
              int ver = t.second, distance = t.first;
              if(st[ver]) continue;//如果已经确定最短就跳过这个节点
              st[ver] = true;
              for(int i = h[ver]; i != -1; i = ne[i] )
              {
                  int j = e[i];//取出节点编号
                  if(dist[j] > distance + w[i])
                  {
                      dist[j] = distance + w[i];
                      heap.push({dist[j],j});
                  }
              }
          }
          if(dist[n] == 0x3f3f3f3f) return -1;
          return dist[n];
      }
      int main()
      {
          scanf("%d%d", &n, &m);
          memset(h, -1, sizeof h);
          while(m -- )
          {
              int a, b, c;
              scanf("%d%d%d", &a, &b, &c);
              add(a,b,c);
          }
          int res = dijkstra();
          printf("%d\n",res);
          return 0;
      }

  3. Bellman_ford:

    • 思路:

      图用结构体实现,算法有两层循环实现,循环之前要初始化,第一层循环n次,第一层的含义是循环了几条边;第二层循环循环m次,更新距离,这里有个关键点就是要备份数组,用备份的数组去更新距离。

      备份在第一与第二层循环之间备份。

    • 例题:

    • 代码:

      #include <bits/stdc++.h>
      ​
      using namespace std;
      ​
      const int N = 510, M = 10010;
      int n,m,k;
      int dist[N];//意义跟上面dijkstra的一样
      bool st[N];
      int backup[N];//备份数组
      ​
      struct edge{
          int a,b,w;
      }edges[M];
      ​
      int bellman_ford()
      {
          memset(dist, 0x3f, sizeof dist);
          dist[1] = 0;
          for(int i = 0; i < k; i ++ )//因为题目限制不超过k条边
          {
              memcpy(backup,dist,sizeof dist);//一定要记得备份
              for(int j = 0; j < m; j ++ )
              {
                  int a = edges[j].a;
                  int b = edges[j].b;
                  int w = edges[j].w;
                  dist[b] = min(dist[b], backup[a] + w); 
              }
          }
          if(dist[n] > 0x3f3f3f3f / 2) return -2;
          return dist[n];
      }
      ​
      int main()
      {
          scanf("%d%d%d",&n,&m,&k);
          for(int i = 0; i < m; i ++ )
          {
              int a,b,w;
              scanf("%d%d%d",&a,&b,&w);
              edges[i] = {a,b,w};
          }
          int t = bellman_ford();
          if(t == -2) puts("impossible");
          else printf("%d\n",t);
          return 0;
      }

  4. SPFA(基本可以解决dijkstra的题,但是如果卡住只能用堆优化的dijkstra;SPFA还可以判断是否有负权回路)

    • 思路:

      对bellman_ford的优化就是用队列实现只对需要更新的点进行更新,代码类似堆优化的dijkstra,只是st数组标注的是点是否已经在队列中,队列存的是待更新的点。

    • 例题:

    • 代码:

      #include <bits/stdc++.h>
      ​
      using namespace std;
      ​
      const int N = 100010;
      ​
      int h[N],w[N],e[N],ne[N],idx;
      int dist[N]; 
      bool st[N];//标志这个点是否已经在队列中
      int n,m;
      ​
      void add(int a,int b,int c)
      {
          e[idx] = b;
          ne[idx] = h[a];
          w[idx] = c;
          h[a] = idx ++ ;
      } 
      ​
      int spfa()
      {
          memset(dist, 0x3f,sizeof dist);
          dist[1] = 0;
          queue<int> q;//队列存放待更新的点 
          q.push(1);
          st[1] = true;
          while(q.size())
          {
              int t = q.front();
              q.pop();
              st[t] = false;
              for(int i = h[t]; i != -1; i = ne[i])
              {
                  int j = e[i];
                  if(dist[j] > dist[t] + w[i])
                  {
                      dist[j] = dist[t] + w[i];
                      if(!st[j])
                      {
                          q.push(j);
                          st[j] = true;
                      }
                  }
              }
          }
          if(dist[n] == 0x3f3f3f3f) return -2;
          return dist[n];
      }
      ​
      int main()
      {
          scanf("%d%d",&n,&m);
          memset(h,-1,sizeof h);
          for(int i = 0; i < m; i ++ )
          {
              int a,b,c;
              scanf("%d%d%d",&a,&b,&c);
              add(a,b,c);
          }
          int res = spfa();
          if(res == -2) puts("impossible");
          else printf("%d\n",res);
          return 0;
      }

  5. Floyd:

    • 思路:

      基于动态规划,三维的数组实现。d[ i ] [ j ] [ k ],表示从 i 开始,经过1 - k个点到达 j 的最短距离,只是最后k被优化掉了,只剩下二维。

    • 例题:

    • 代码:

      #include <bits/stdc++.h>
      ​
      using namespace std;
      ​
      const int N = 210, INF = 1e9;
      ​
      int n,m,k;
      int d[N][N];
      ​
      void floyd()
      {
          for(int k = 1; k <= n; k ++ )
              for(int i = 1; i <= n; i ++ )
                  for(int j = 1; j <= n; j ++ )
                      d[i][j] = min(d[i][j],d[i][k] + d[k][j]);
      }
      ​
      int main()
      {
          scanf("%d%d%d",&n,&m,&k);
          for(int i = 1; i <= n; i ++ )
              for(int j = 1; j <= n; j ++ )
                  if(i == j) d[i][j] = 0;
                  else d[i][j] = INF;//这里的初始化有区别与上面几种算法,因为它是多源的
          for(int i = 0; i < m; i ++ )
          {
              int x,y,z;
              scanf("%d%d%d",&x,&y,&z);
              d[x][y] = min(d[x][y],z);//存最小的边即可
          }
          floyd();
          while(k -- )
          {
              int x,y;
              scanf("%d%d",&x,&y);
              if(d[x][y] > INF / 2) puts("impossible");//因为有负的,所以只需大于 INF / 2
              else cout << d[x][y] << endl;
          }
          return 0;
      }

以上是个人的一些看法,如果有错误的或者更好的做法,欢迎评论区给予更正谢谢!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值