zoukankan      html  css  js  c++  java
  • 最短路算法--模板

    迪杰斯特拉算法(Dijkstra):

    Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。

    主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。

      1 #include <iostream>
      2 using namespace std; 
      3 const int maxnum = 100;
      4 const int maxint = 999999;
      5  
      6 // 各数组都从下标1开始
      7 int dist[maxnum];     // 表示当前点到源点的最短路径长度
      8 int prev[maxnum];     // 记录当前点的前一个结点
      9 int c[maxnum][maxnum];   // 记录图的两点间路径长度
     10 int n, line;             // 图的结点数和路径数
     11  
     12 // n -- n nodes
     13 // v -- the source node
     14 // dist[] -- the distance from the ith node to the source node
     15 // prev[] -- the previous node of the ith node
     16 // c[][] -- every two nodes' distance
     17 void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
     18 {
     19      bool s[maxnum];    // 判断是否已存入该点到S集合中
     20      for(int i=1; i<=n; ++i)
     21      {
     22           dist[i] = c[v][i];
     23           s[i] = 0;     // 初始都未用过该点
     24           if(dist[i] == maxint)
     25            prev[i] = 0;
     26           else
     27            prev[i] = v;
     28      }
     29      dist[v] = 0;
     30      s[v] = 1;
     31      // 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
     32      // 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
     33         // 注意是从第二个节点开始,第一个为源点
     34      for(int i=2; i<=n; ++i)
     35      {
     36           int tmp = maxint;
     37           int u = v;
     38           // 找出当前未使用的点j的dist[j]最小值
     39           for(int j=1; j<=n; ++j)
     40            if((!s[j]) && dist[j]<tmp)
     41            {
     42                 u = j;              // u保存当前邻接点中距离最小的点的号码
     43                 tmp = dist[j];
     44            }
     45           s[u] = 1;    // 表示u点已存入S集合中
     46          
     47           // 更新dist
     48           for(int j=1; j<=n; ++j)
     49            if((!s[j]) && c[u][j]<maxint)
     50            {
     51                 int newdist = dist[u] + c[u][j];
     52                 if(newdist < dist[j])
     53                 {
     54                      dist[j] = newdist;
     55                      prev[j] = u;
     56                 }
     57            }
     58      }
     59 } 
     60 // 查找从源点v到终点u的路径,并输出(写题时可以省略)
     61 void searchPath(int *prev,int v, int u)
     62 {
     63      int que[maxnum];
     64      int tot = 1;
     65      que[tot] = u;
     66      tot++;
     67      int tmp = prev[u];
     68      while(tmp != v)
     69      {
     70           que[tot] = tmp;
     71           tot++;
     72           tmp = prev[tmp];
     73      }
     74      que[tot] = v;
     75      for(int i=tot; i>=1; --i)
     76       if(i != 1)
     77        cout << que[i] << " -> ";
     78       else
     79        cout << que[i] << endl;
     80 }
     81  
     82 int main()
     83 {
     84  
     85  cin >> n; // 输入结点数
     86  cin >> line; // 输入路径数
     87  int p, q, len;  // 输入p, q两点及其路径长度
     88  
     89  // 初始化c[][]为maxint
     90  for(int i=1; i<=n; ++i)
     91   for(int j=1; j<=n; ++j)
     92    c[i][j] = maxint;
     93  
     94  for(int i=1; i<=line; ++i)  
     95  {
     96       cin >> p >> q >> len;
     97       if(len < c[p][q])       // 有重边
     98       {
     99            c[p][q] = len;      // p指向q
    100            c[q][p] = len;      // q指向p,这样表示无向图
    101       }
    102  }
    103  for(int i=1; i<=n; ++i)
    104   dist[i] = maxint;
    105  for(int i=1; i<=n; ++i)
    106  {
    107       for(int j=1; j<=n; ++j)
    108        printf("%8d", c[i][j]);
    109       printf("
    ");
    110  }
    111  
    112  Dijkstra(n, 1, dist, prev, c);
    113  //最短路径长度
    114  cout << "源点到最后一个顶点的最短路径长度: " << dist[n] << endl;
    115  //路径
    116  cout << "源点到最后一个顶点的路径为: ";
    117  searchPath(prev, 1, n);
    118 }
    View Code
     1 #include <stdio.h>
     2 #include <string.h>
     3 #define inf 99999999
     4 #define MAX 100
     5 
     6 int n; 
     7 int dis[MAX];
     8 int path[MAX][MAX];
     9 int visit[MAX];
    10 int dijkstra () //起点从1开始  输出起点到终点的最短路径 
    11 {
    12     int i,j,pos,minn;
    13     for (i = 1; i <= n; i ++)
    14     {
    15         dis[i] = path[1][i];
    16         visit[i] = 0;
    17     }
    18     dis[1] = 0;
    19     visit[1] = 1;
    20     for(i = 1;i <= n; i ++)
    21     {
    22         minn = inf;
    23         for (j = 1; j <= n; j ++)
    24             if (!visit[j] && dis[j] < minn)
    25             {
    26                 minn = dis[j];
    27                 pos = j;
    28             }    
    29         visit[pos] = 1;
    30         for (j = 1; j <= n; j ++)
    31         {
    32             if (!visit[j] && dis[j] > dis[pos]+path[pos][j])
    33                 dis[j] = dis[pos]+path[pos][j];
    34         }
    35     }
    36     return dis[n]; 
    37 }

    弗洛伊德算法Floyd算法):

    Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,

    同时也被用于计算有向图的传递闭包。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。

    a.从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。   

    b.对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。

     1 #include <stdio.h>
     2 #include <string.h>
     3 #define inf 99999999
     4 #define MAX 100
     5 
     6 int n;
     7 int path[MAX][MAX];
     8 int floyd() //任意两点之间的最短路径 
     9 {
    10     int i,j,k;
    11     for (k = 1; k <= n; k ++)
    12         for (i = 1; i <= n; i ++)
    13             for (j = 1; j <= n; j ++)
    14                 if (path[i][j] > path[i][k]+path[k][j])
    15                     path[i][j] = path[i][k]+path[k][j];
    16     return path[1][n];
    17 }

    Mellman-Frod算法:

    适用条件&范围:

    单源最短路径(从源点s到其它所有顶点v);

    有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图);

    边权可正可负(如有负权回路输出错误提示);

    Bellman-Ford算法可以大致分为三个部分
    第一,初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。
    第二,进行循环,循环下标为从1到n-1(n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。
    第三,遍历途中所有的边(edge(u,v)),判断是否存在这样情况:
    d(v) > d (u) + w(u,v)
    则返回false,表示途中存在从源点可达的权为负的回路。

     1 #include<iostream>  
     2 #include<cstdio>  
     3 using namespace std;  
     4   
     5 #define MAX 0x3f3f3f3f  
     6 #define N 1010  
     7 int nodenum, edgenum, original; //点,边,起点  
     8   
     9 typedef struct Edge //
    10 {  
    11     int u, v;  
    12     int cost;  
    13 }Edge;  
    14   
    15 Edge edge[N];  
    16 int dis[N], pre[N];  
    17   
    18 bool Bellman_Ford()  
    19 {  
    20     for(int i = 1; i <= nodenum; ++i) //初始化  
    21         dis[i] = (i == original ? 0 : MAX);  
    22     for(int i = 1; i <= nodenum - 1; ++i)  
    23         for(int j = 1; j <= edgenum; ++j)  
    24             if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //松弛(顺序一定不能反~)  
    25             {  
    26                 dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;  
    27                 pre[edge[j].v] = edge[j].u;  
    28             }  
    29             bool flag = 1; //判断是否含有负权回路  
    30             for(int i = 1; i <= edgenum; ++i)  
    31                 if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost)  
    32                 {  
    33                     flag = 0;  
    34                     break;  
    35                 }  
    36                 return flag;  
    37 }  
    38   
    39 void print_path(int root) //打印最短路的路径(反向)  
    40 {  
    41     while(root != pre[root]) //前驱  
    42     {  
    43         printf("%d-->", root);  
    44         root = pre[root];  
    45     }  
    46     if(root == pre[root])  
    47         printf("%d
    ", root);  
    48 }  
    49   
    50 int main()  
    51 {  
    52     scanf("%d%d%d", &nodenum, &edgenum, &original);  
    53     pre[original] = original;  
    54     for(int i = 1; i <= edgenum; ++i)  
    55     {  
    56         scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost);  
    57     }  
    58     if(Bellman_Ford())  
    59         for(int i = 1; i <= nodenum; ++i) //每个点最短路  
    60         {  
    61             printf("%d
    ", dis[i]);  
    62             printf("Path:");  
    63             print_path(i);  
    64         }  
    65     else  
    66         printf("have negative circle
    ");  
    67     return 0;  
    68 }  
    View Code
     1 #include <stdio.h>
     2 #include <string.h>
     3 #define inf 99999999
     4 #define MAX 100
     5 
     6 struct land 
     7 {
     8     int x,y,z;        
     9 }path[MAX]; 
    10 void add(int u,int v,int w) //从u到v路径为w
    11 {
    12     path[ans].x = u;
    13     path[ans].y = v;
    14     path[ans].z = w;
    15     ans ++;
    16 } 
    17 int n; //n个节点
    18 int ans; //ans条路 
    19 int path[MAX][MAX];
    20 bool Bellman_ford() //判断是否出现正环或负环
    21 {
    22     int i,j;
    23     for (i = 1; i <= n; i ++)
    24         dis[i] = inf;
    25     dis[1] = 0;
    26     
    27     for (i = 1; i < n; i ++)  //n-1次循环
    28     {
    29         bool flag = false;  //优化 
    30         for(j = 0; j < m; j ++)  //松弛m条路
    31             if(dis[path[j].y] > dis[path[j].x]+path[j].z)
    32             {
    33                 dis[path[j].y] = dis[path[j].x]+path[j].z;
    34                 flag = true;
    35             }
    36         if (!flag)  //不能松弛,表示一定不存在负权值回路 
    37             return false;
    38     }
    39     
    40     for (j = 0; j < m; j ++)
    41         if (dis[path[j].y] > dis[path[j].x]+path[j].z)
    42             return true;
    43     return false;
    44 } 

    SPFA算法:

    推荐博客:http://www.cnblogs.com/scau20110726/archive/2012/11/18/2776124.html

         http://blog.csdn.net/muxidreamtohit/article/details/7894298

    适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。 我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。

    算法思想:我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止

    以POJ1511为例

      1 #include <stdio.h>
      2 #include <string.h>
      3 #define inf 9999999999
      4 #include <iostream>
      5 #include <queue>
      6 #include <algorithm>
      7 using namespace std;
      8 struct node
      9 {
     10     int to;
     11     int w;
     12     int next;
     13 };
     14 queue <int > q;
     15 int n,m;
     16 node list[1000010];
     17 node list1[1000010];
     18 int vis[1000010];
     19 int dis[1000010];
     20 int h1[1000010];
     21 int h2[1000010];
     22 void spfa()
     23 {
     24     int i,j,u;
     25     for (i = 1; i <= n; i ++)
     26     {
     27         dis[i] = inf;
     28         vis[i] = 0;
     29     }
     30     q.push(1);
     31     dis[1] = 0;
     32     vis[1] = 1;
     33 
     34     while (!q.empty())
     35     {
     36         u = q.front();
     37         q.pop();
     38         vis[u] = 0;
     39         for (j = h1[u]; j ; j = list[j].next)
     40         {
     41             if (dis[list[j].to] > dis[u]+list[j].w)
     42             {
     43                 dis[list[j].to] = dis[u]+list[j].w;
     44                 if (!vis[list[j].to])
     45                 {
     46                     q.push(list[j].to);
     47                     vis[list[j].to] = 1;
     48                 }
     49             }
     50         }
     51     }
     52 }
     53 void spfa1()
     54 {
     55     int i,j,u;
     56     for (i = 1; i <= n; i ++)
     57     {
     58         dis[i] = inf;
     59         vis[i] = 0;
     60     }
     61     q.push(1);
     62     dis[1] = 0;
     63     vis[1] = 1;
     64 
     65     while (!q.empty())
     66     {
     67         u = q.front();
     68         q.pop();
     69         vis[u] = 0;
     70         for (j = h2[u]; j ; j = list1[j].next)
     71         {
     72             if (dis[list1[j].to] > dis[u]+list1[j].w)
     73             {
     74                 dis[list1[j].to] = dis[u]+list1[j].w;
     75                 if (!vis[list1[j].to])
     76                 {
     77                     q.push(list1[j].to);
     78                     vis[list1[j].to] = 1;
     79                 }
     80             }
     81         }
     82     }
     83 }
     84 int main ()
     85 {
     86     int i,j,t,u,v,w,ans;
     87     scanf("%d",&t);
     88     while (t --)
     89     {
     90         scanf("%d%d",&n,&m);
     91         memset(h1,0,sizeof(h1));
     92         memset(h2,0,sizeof(h2));
     93         for (ans = 1,i = 0; i < m; i ++)
     94         {
     95             scanf("%d%d%d",&u,&v,&w);
     96             node temp = {v,w,0};
     97             list[ans] = temp;
     98             list[ans].next = h1[u];
     99             h1[u] = ans;
    100             temp.to = u;
    101             list1[ans] = temp;
    102             list1[ans].next = h2[v];
    103             h2[v] = ans;
    104             ans ++;
    105         }
    106         long long sum = 0;
    107         spfa();
    108         for (i = 1; i <= n; i ++)
    109             sum += dis[i];
    110         spfa1();
    111         for (i = 1; i <= n; i ++)
    112             sum += dis[i];
    113         printf("%lld
    ",sum);
    114     }
    115     return 0;
    116 }
    View Code
  • 相关阅读:
    参考文献bib管理
    linux开启防火墙指定端口
    Linux rabbitmq 新增用户和角色
    JAVA导出Excel并弹出下载框
    Base64 文件图片 加密解密 【java】
    Minio-JAVA使用
    Linux下Minio搭建
    ORACLE跨越时间点的恢复
    重做日志损坏之后的处理
    转:关于PLSQL Developer报"动态执行表不可访问,本会话的自动统计被禁止"错的解决方法
  • 原文地址:https://www.cnblogs.com/yoke/p/5875996.html
Copyright © 2011-2022 走看看