zoukankan      html  css  js  c++  java
  • 图论算法->最短路

    求最短路算法,有Floyd,dijkstra,Bellmanford,spfa等诸多高级算法。优化方法也是层出不穷。

    我们不妨分析一下各算法的使用特点(可能不准确

    1.Floyd算法 复杂度O(n³)可计算任意两点间最短路径 可处理负边权情况

    2.Dijkstra算法 复杂度O(n²)只能计算单源最短路径 不可处理负边权情况(多源可再加一重循环)

    3.Bellman-Ford算法 复杂度O(边数*点数) 计算单源最短路径 能处理负边权情况但无法处理存在负权回路情况

    4.spfa算法 复杂度O(边数*玄学常数) 单源最短路径 可处理负边权情况

    另外,这种算法在稀疏图上是好,而在稠密图有不乐观的一面

    另外,还有输出最短路径方案,记录前驱。参见玛丽卡一题。

    我们不妨先来讨论两种图的存储方式

    第一种是邻接矩阵,开了一个二维数组。如f[i][j]记录了i到j有一条边,且权值为f[i][j]

    第二种是邻接表。用一个结构体+head数组记录这条边的信息

     1 struct node{
     2     int val,to,next;
     3     //node存储第i条边的信息,val为第i条边的权值,to为第i条边的终
     4     //点,next为同一起点的下一条边的第几条边数 
     5 }edge[2005]; 
     6 int head[2005];//head[x]表示以x节点为起点的第一条边的边数 
     7 int tot;//tot记录已经读到第几条边 
     8 
     9 /*建边*/
    10 void build(int x,int y,int z)
    11 {
    12     tot++;
    13     edge[tot].val=z;
    14     edge[tot].to=y;
    15     edge[tot].next=head[x];//这里好像链表一样 
    16     head[x]=tot;
    17 } 

    我们可以用这张图帮助理解。

    长得有点像链表 是不是耶...

    一、最暴力的弗洛伊德Floyd

     1 //floyd 
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 const int inf=0x7f7f7f7f;
     5 int n,m;
     6 int w[2000][2000];
     7 int dis[2000][2000];
     8 int main()
     9 {
    10     scanf("%d%d",&n,&m);
    11     for(int i=1;i<=n;i++)
    12     {
    13         for(int j=1;j<=n;j++)
    14          dis[i][j]=inf;
    15     }
    16     for(int i=1;i<=m;i++)
    17     {
    18         int u,v;
    19         scanf("%d%d%d",&u,&v,&w[u][v]);
    20         dis[u][v]=w[u][v];
    21     }
    22     //floyd
    23     for(int k=1;k<=n;k++)
    24      for(int i=1;i<=n;i++)
    25       for(int j=1;j<=n;j++)
    26        if(dis[i][j]>dis)
    27         dis[i][j]=dis[i][k]+dis[k][j];
    28     //输出 dis[i][j]为i到j的最短路 
    29     return 0;
    30 }

    二、Dijkstra+堆优化+邻接表

     1 // Dijkstra
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 int n,m,s;
     5 const int inf=0x3f3f3f3f;
     6 int tot;
     7 bool visit[500005];
     8 int head[500005],dis[500005];
     9 struct node{
    10     int val,to,next;
    11 }edge[500005];
    12 priority_queue<  pair<int,int>  > q;
    13 //大根堆 pair第一维为dis相反数以达到小根堆效果,第二维为节点编号 
    14 void add(int x,int y,int z)
    15 {
    16     tot++;
    17     edge[tot].to=y;
    18     edge[tot].val=z;
    19     edge[tot].next=head[x];
    20     head[x]=tot;
    21 }
    22 void dijkstra()
    23 {
    24     for(int i=1;i<=n;i++)
    25      dis[i]=inf;
    26     dis[s]=0;//这里默认把1作为起点,求多源时可另加调整
    27     q.push(make_pair(0,s));
    28     while(!q.empty())
    29     {
    30         int x=q.top().second;
    31         q.pop();//pop会把二元组中两个元素一并弹出 
    32         if(visit[x]) continue;
    33         visit[x]=1;
    34         for(int i=head[x];i;i=edge[i].next)
    35         {
    36             int y=edge[i].to;
    37             int z=edge[i].val;
    38             if(dis[y]>dis[x]+z&&visit[y]==0)
    39             {
    40                 dis[y]=dis[x]+z;
    41                 q.push(make_pair(-dis[y],y));
    42             }
    43          } 
    44     }
    45 }
    46 int main()
    47 {
    48     scanf("%d%d%d",&n,&m,&s);
    49     for(int i=1;i<=m;i++)
    50     {
    51         int x=0,y=0,z=0;
    52         scanf("%d%d%d",&x,&y,&z);
    53         add(x,y,z);
    54     }
    55     dijkstra();
    56     for(int i=1;i<=n;i++)
    57      printf("%d ",dis[i]);
    58     return 0;
    59 }

    三、SPFA算法(大力拒绝bellmanford

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<queue>
     6 using namespace std;
     7 const int inf=2147483647;
     8 int n,m,s;
     9 int num;
    10 int pre[20000];
    11 struct node{
    12     int to,val,next;
    13 }edge[500005];
    14 int dis[20000];
    15 bool visit[20000];
    16 void add(int x,int y,int z)
    17 {
    18     num++;
    19     edge[num].val=z;
    20     edge[num].to=y;
    21     edge[num].next=pre[x];
    22     pre[x]=num;
    23 }
    24 void spfa()
    25 {
    26     queue<int>q;
    27     for(int i=1;i<=n;i++)
    28     {
    29         dis[i]=inf;
    30     }
    31     q.push(s);
    32     dis[s]=0;
    33     visit[s]=1;
    34     while(!q.empty())
    35     {
    36         int u=q.front();
    37         q.pop();
    38         visit[u]=0;
    39         for(int i=pre[u];i>0;i=edge[i].next)
    40         {
    41             int v=edge[i].to;
    42             if(dis[v]>dis[u]+edge[i].val)
    43             {
    44                 dis[v]=dis[u]+edge[i].val;
    45                 if(visit[v]==0)
    46                 {
    47                     visit[v]=1;
    48                     q.push(v);
    49                 }
    50             }
    51             
    52         }
    53     }
    54 }
    55 int main()
    56 {
    57     scanf("%d%d%d",&n,&m,&s);
    58     for(int i=1;i<=m;i++)
    59     {
    60         int x=0,y=0,z=0;
    61         scanf("%d%d%d",&x,&y,&z);
    62         add(x,y,z);
    63     }
    64     spfa();
    65     for(int i=1;i<=n;i++)
    66     {
    67         if(s==i) cout<<"0"<<" ";
    68         else cout<<dis[i]<<" ";
    69     }
    70     
    71     
    72     return 0;
    73 }

    最后我们再来讨论一下防止溢出的问题。

    建邻接表的时候,edge开到边数,无向图开二倍边;

    head dis visit均存到点数即可。

  • 相关阅读:
    转载一个好用的div弹出层插件
    asp.net 母版页使用方法
    visual studio 代码排版组合键
    模仿米折网商品图片自动翻页效果
    BinaryWriter 、BinaryReader在读写
    Java 8 Lambda 表达式
    IBeacon协议分析
    Centos配置jdk和tomcat环境
    apidoc 生成Restful web Api文档
    数组和链表的区别
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/8677257.html
Copyright © 2011-2022 走看看