zoukankan      html  css  js  c++  java
  • 关于最短路径问题(图论)

    模版题为【hdu 2544】最短路。比较详细的解释请见:【转】彻底弄懂最短路径问题(图论)

    前言:我先写一些总结性的话——
    1.推荐使用优先队列优化后的Dijkstra算法,速度快又稳定,而SPFA算法虽快但不稳定;但也有特殊情况,譬如说:【uva 658】It's not a Bug, it's a Feature!(图论--Dijkstra或spfa算法+二进制表示+类“隐式图搜索”)这些类“隐式图搜索”的题。然而......似乎我们这边大家都喜欢用SPFA,因为最初接触的就是它而且它就是一个BFS,比较好打;
    2.出现负边和判断负环都用Bellman-Ford算法(也就是SPFA算法);

    3.Floyd算法本质是DP或贪心思想,枚举出了所有路径的情况,一些“合法性”“可到达性”的题目可以用它。

    1.Dijkstra算法

    概述:依次将当前没有标记过的,且与源点的距离最近的点标记 / 纳入联盟内,并拓展该点,更新与之相连边的所有另一点的离源点的距离。   P.S.之前我以为几乎就是MST中的Prim算法;这个原理我真心不怎么理解 _(눈_ 눈」∠)_
    实现:邻接表+优先队列。
    时间复杂度:O(n^2+m); O(n log n+m)。
    应用:有向图和无向图,正权图上的单源最短路(Single-Source Shortest Paths, SSSP,即从单个源点出发到所有结点的最短路)。
    注意——正权图上可能有零环和正环,但都不会影响最短路的计算;Dijkstra中优先队列的top()是汇点的值时就可以跳出,因为剩下的可以拓展的点都比它的值大,由于没有负权边,就不可能通过一些边又比它小了。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 
     7 const int N=110,M=10010,D=1010,INF=(int)1e9;
     8 int n,m;
     9 int last[N],d[N],vis[N];
    10 struct edge{int x,y,d,next;}a[2*M];
    11 
    12 int mmin(int x,int y) {return x<y?x:y;}
    13 void ins(int len,int x,int y,int d)
    14 {
    15     a[len].x=x,a[len].y=y,a[len].d=d;
    16     a[len].next=last[x],last[x]=len;
    17 }
    18 int Dijkstra()
    19 {
    20     memset(vis,0,sizeof(vis));
    21     memset(d,63,sizeof(d));
    22     d[1]=0;
    23     for (int k=1;k<=n;k++)//加n个点进联盟
    24     {
    25       int p=0,mn=INF;
    26       for (int i=1;i<=n;i++)
    27         if (!vis[i] && d[i]<mn) p=i,mn=d[i];
    28       vis[p]=1;
    29       for (int i=last[p];i;i=a[i].next)
    30       {
    31         int y=a[i].y;
    32         d[y]=mmin(d[y],d[p]+a[i].d);
    33       }
    34     }
    35     return d[n];
    36 }
    37 int main()
    38 {
    39     while (1)
    40     {
    41       scanf("%d%d",&n,&m);
    42       if (!n && !m) break;
    43       memset(last,0,sizeof(last));
    44       for (int i=1;i<=m;i++)
    45       {
    46         int x,y,d;
    47         scanf("%d%d%d",&x,&y,&d);
    48         ins(2*i-1,x,y,d),ins(2*i,y,x,d);
    49       }
    50       printf("%d
    ",Dijkstra());
    51     }
    52     return 0;
    53 }
    54 
    55 Dijkstra
    Dijkstra
     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 #include<queue>
     6 using namespace std;
     7 
     8 const int N=110,M=10010,D=1010,INF=(int)1e9;
     9 int n,m;
    10 int last[N],vis[N],d[N];//,path[N];
    11 struct edge{int x,y,d,next;}a[2*M];
    12 struct node
    13 {
    14     int x,d;
    15     bool operator < (const node& now) const
    16     { return d>now.d; }// > 使原来的优先队列排序反转,dmin first out
    17     node() {}
    18     node(int u,int v) {x=u;d=v;}
    19 };
    20 priority_queue<node> q;//默认从大到小排序
    21 
    22 int mmin(int x,int y) {return x<y?x:y;}
    23 void ins(int len,int x,int y,int d)
    24 {
    25     a[len].x=x,a[len].y=y,a[len].d=d;
    26     a[len].next=last[x],last[x]=len;
    27 }
    28 int Dijkstra()//!!!important!!
    29 {
    30     while (!q.empty()) q.pop();
    31     memset(vis,0,sizeof(vis));
    32     memset(d,63,sizeof(d));
    33     d[1]=0;
    34     q.push(node(1,0)); //这里无 vis[x]=1;
    35     while (!q.empty())
    36     {
    37       node now=q.top(); q.pop();
    38       int x=now.x,tmp=now.d;
    39       if (x==n) return d[x];//见“注意”
    40       if (vis[x]) continue;//或 if (tmp!=d[x]) continue; 表示标记过了,不再用它更新相关的点,以防止重复拓展
    41       vis[x]=1;
    42       for (int i=last[x];i;i=a[i].next)
    43       {
    44         int y=a[i].y;
    45         if (d[x]+a[i].d<d[y])
    46         {
    47           d[y]=d[x]+a[i].d;
    48           //path[y]=x;
    49           q.push(node(y,d[y]));//no judge
    50         }
    51       }
    52     }
    53     return -1;
    54 }
    55 int main()
    56 {
    57     while (1)
    58     {
    59       scanf("%d%d",&n,&m);
    60       if (!n && !m) break;
    61       memset(last,0,sizeof(last));
    62       for (int i=1;i<=m;i++)
    63       {
    64         int x,y,d;
    65         scanf("%d%d%d",&x,&y,&d);
    66         ins(2*i-1,x,y,d),ins(2*i,y,x,d);
    67       }
    68       printf("%d
    ",Dijkstra());
    69     }
    70     return 0;
    71 }
    72 
    73 Dijkstra+优先队列(important!!)
    Dijkstra+优先队列(important!!)

    附上一个小笑话 ヾ(o◕∀◕)ノヾ Dijkstra这么厉害,为什么没有把求多点之间的最短路的Floyd算法也给发明了。——原因是,它叫Dijkstra,不叫Dkijstra。··(≧∀≦)··

    2.Bellman-ford算法

    概述:由于最多只包含n-1个结点,便通过n-1轮,对m条边进行松弛,更新端点的值。
    实现:队列。

    时间复杂度:O(nm)。
    应用:有向图和无向图,正权和负权图的最短路,判断负环;若要算出单源最短路,需要让源点和每个点之间加一条权值为0的边,这样每个点都可以被访问。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 #include<queue>
     6 using namespace std;
     7 
     8 const int N=110,M=10010,D=1010,INF=(int)1e9;
     9 int n,m;
    10 int last[N],vis[N],d[N];//,path[N];
    11 struct edge{int x,y,d,next;}a[2*M];
    12 
    13 int mmin(int x,int y) {return x<y?x:y;}
    14 void ins(int len,int x,int y,int d)
    15 {
    16     a[len].x=x,a[len].y=y,a[len].d=d;
    17     a[len].next=last[x],last[x]=len;
    18 }
    19 int Bellman_ford()//!!!important!!
    20 {
    21     memset(vis,0,sizeof(vis));
    22     memset(d,63,sizeof(d));
    23     d[1]=0;
    24     for (int k=1;k<n;k++)
    25       for (int i=1;i<=m;i++)
    26       {
    27         int x=a[i].x,y=a[i].y;
    28         d[y]=mmin(d[y],d[x]+a[i].d);
    29       }
    30     return d[n];
    31 }
    32 int main()
    33 {
    34     while (1)
    35     {
    36       scanf("%d%d",&n,&m);
    37       if (!n && !m) break;
    38       memset(last,0,sizeof(last));
    39       for (int i=1;i<=m;i++)
    40       {
    41         int x,y,d;
    42         scanf("%d%d%d",&x,&y,&d);
    43         ins(2*i-1,x,y,d),ins(2*i,y,x,d);
    44       }
    45       m=2*m;
    46       printf("%d
    ",Bellman_ford());
    47     }
    48     return 0;
    49 }
    Bellman-ford算法

    3.SPFA算法(Shortest Path Faster Algorithm)

    概述:如它的名字,它其实是在Bellman-ford算法的基础上加上一个队列优化,减少了冗余的松弛操作
    实现:队列。
    时间复杂度:O(km),k为每个节点进入队列的次数,且k一般<=2;最坏情况是O(nm),比如图为完全图时会有n-1条边的松弛路径,每个点会入队n-1次。{详见Dijkstra、Bellman_Ford、SPFA、Floyd算法复杂度比较。}
    应用:有向图和无向图,正权和负权图上的单源最短路,判断负环。拓展:若要算出单源最短路,需要让源点和每个点之间加一条权值为0的边,这样每个点都可以被访问。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 #include<queue>
     6 using namespace std;
     7 
     8 const int N=110,M=10010,D=1010,INF=(int)1e9;
     9 int n,m;
    10 int last[N],inq[N],cnt[N],d[N];//,path[N];
    11 struct edge{int x,y,d,next;}a[2*M];
    12 queue<int> q;
    13 
    14 int mmin(int x,int y) {return x<y?x:y;}
    15 void ins(int len,int x,int y,int d)
    16 {
    17     a[len].x=x,a[len].y=y,a[len].d=d;
    18     a[len].next=last[x],last[x]=len;
    19 }
    20 int spfa()
    21 {
    22     while (!q.empty()) q.pop();
    23     memset(d,63,sizeof(d));
    24     memset(inq,0,sizeof(inq));
    25     memset(cnt,0,sizeof(cnt));
    26     q.push(1);
    27     inq[1]=cnt[1]=1,d[1]=0;
    28     while (!q.empty())
    29     {
    30       int x=q.front();
    31       q.pop(), inq[x]=0;
    32       for (int i=last[x];i;i=a[i].next)
    33       {
    34         int y=a[i].y;
    35         if (d[x]+a[i].d<d[y])
    36         {
    37           d[y]=d[x]+a[i].d;
    38           //path[y]=x; 或 path[y]=i;
    39           if (!inq[y])
    40           {
    41             inq[y]=1,cnt[y]++;
    42             if (cnt[y]>n) return -1;//> 判断负圈,一个点被更新了超过n次
    43             q.push(y);
    44           }
    45         }
    46       }
    47     }
    48     return d[n];
    49 }
    50 int main()
    51 {
    52     while (1)
    53     {
    54       scanf("%d%d",&n,&m);
    55       if (!n && !m) break;
    56       memset(last,0,sizeof(last));
    57       for (int i=1;i<=m;i++)
    58       {
    59         int x,y,d;
    60         scanf("%d%d%d",&x,&y,&d);
    61         ins(2*i-1,x,y,d),ins(2*i,y,x,d);
    62       }
    63       printf("%d
    ",spfa());
    64     }
    65     return 0;
    66 }
    Spfa(important!!)

    唉,其实用 bfs 的 spfa 判断负环很慢!应该用 dfs 的。具体解释和代码请见:【洛谷 P3385】模板-负环(图论--spfa)

    4.Floyd-warshall算法

    概述:枚举所有情况,限制每两点中间通过的结点范围而松弛n轮。
    实现:DP思想,递推。
    时间复杂度:O(n^3)。
    应用:有向图和无向图,正权和负权图上的每两点之间的最短路问题 和 连通性结果为有向图的传递闭包(Transitive Closure. 数学上定义为:在集合X上的二元关系R的传递闭包是包含R的X上的最小传递关系)问题。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 #include<queue>
     6 using namespace std;
     7 
     8 const int N=110,M=10010,D=1010,INF=(int)1e9;
     9 int n,m;
    10 int d[N][N];
    11 
    12 int mmin(int x,int y) {return x<y?x:y;}
    13 int Floyd()
    14 {
    15     for (int k=1;k<=n;k++)
    16      for (int i=1;i<=n;i++)
    17       for (int j=1;j<=n;j++)//此时的d[i][j]表示i,j间只能经过1~k点时的最短路径
    18         d[i][j]=mmin(d[i][j],d[i][k]+d[k][j]);//原3维方程:d[k][i][j]=mmin(d[k][i][j],d[k-1][i][k]+d[k-1][k][j]);
    19     return d[1][n];
    20 }
    21 int main()
    22 {
    23     while (1)
    24     {
    25       scanf("%d%d",&n,&m);
    26       if (!n && !m) break;
    27       memset(d,63,sizeof(d));
    28       for (int i=1;i<=m;i++)
    29       {
    30         int x,y,t;
    31         scanf("%d%d%d",&x,&y,&t);
    32         d[x][y]=d[y][x]=t;
    33       }
    34       printf("%d
    ",Floyd());
    35     }
    36     return 0;
    37 }
    Floyd算法
  • 相关阅读:
    redis常见面试题
    nginx常见的面试题
    python学习笔记(15)pymysql数据库操作
    python中的if not
    python学习笔记(24)-类与对象
    python学习笔记(23)-异常处理
    python学习笔记(22)-os文件操作模块
    Maven---pom.xml配置详解
    maven+jmeter+jenkins集成
    适配器模式
  • 原文地址:https://www.cnblogs.com/konjak/p/6031912.html
Copyright © 2011-2022 走看看