• 最短路径 Dijkstra && Bellman_ford


     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 #include<queue>
     5 #include<cstring> 
     6 #define MAX 9999999
     7 
     8 using namespace std;
     9 
    10 int G[203][203];//二维数组图的存储 
    11 int n,s,t;//n点数 s起点 t终点 
    12 
    13 void dijkstra()
    14 {
    15     bool vis[203];//相当于集合Q的功能,标记该点是否访问过
    16     int dis[203];///保存最短路径
    17     int i,j,k;
    18     
    19     for(i=0;i<n;i++)
    20         dis[i]=G[s][i];//s->各个点的距离
    21     memset(vis,false,sizeof(vis));//初始为假 ,表示未访问过
    22     dis[s]=0;//s->s点距离 s点为起点
    23     vis[s]=true;//s点访问过,标记为真
    24     for(i=1;i<n;i++)//G.v-1次操作+上次s点的访问=G.v次操作 
    25     {
    26         k=-1;
    27         for(j=0;j<n;j++)//从尚未访问过的点中选一个距离最小的点 
    28         {
    29             if(!vis[j]&&(k==-1||dis[k]>dis[j]))    //该点未访问过&&距离最小的 
    30                 k=j;
    31         }    
    32         if(k==-1)//若图是不连通的则提前结束 
    33             break; 
    34         vis[k]=true;//将k点标记为访问过的
    35         for(j=0;j<n;j++) //松弛操作 
    36         if(!vis[j]&&dis[j]>dis[k]+G[k][j])  //该点未访问&&可以松弛 
    37             dis[j]=dis[k]+G[k][j]; //j点的距离大于当前点的距离+w(k,j),则松弛成功,更新
    38              
    39     }    
    40     printf("%d
    ",dis[t]==MAX?-1:dis[t]);
    41 } 
    42 
    43 int main()
    44 {
    45     int m,i,j,u,v,w;
    46     
    47     while(~scanf("%d%d",&n,&m))
    48     {
    49         for(i=0;i<n;i++)
    50              for(j=0;j<n;j++)
    51                   G[i][j]=i==j?0:MAX;//初始化,本身到本身为0,否则为MAX
    52          while(m--)
    53          {
    54              scanf("%d%d%d",&u,&v,&w);
    55              if(G[u][v]>w)//因为初始化操作
    56                  G[u][v]=G[v][u]=w;//无向图 双向
    57             
    58           } 
    59           scanf("%d%d",&s,&t); 
    60           dijkstra();
    61     }
    62     return 0;
    63 }
    普通Dijkstra算法

    eg:http://acm.hdu.edu.cn/showproblem.php?pid=1874

     题意:多组输入。第一行给你两个数n(代表点),m(代表边)

        第2—m+1行 ,每行三个数u,v,  w。0<=u,v<n,  w>=0;

        第m+2行两个数 s, t  。 s为源点,t为要到达的目的点。

        求s到t 的最短路,若存在最短路输出最短路的值,否则输出-1。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <iostream>
     5 #include <queue>
     6 #define MAX 9999999
     7 
     8 using namespace std;
     9 //pair 的first 保存的为最短距离, second保存的为顶点编号
    10 typedef pair<int, int >P;//对组  不知道请自行百度   
    11 
    12 struct node
    13 {
    14     int v, w;//v 为到达的点, w为权重
    15     int next;//记录下一个结构体的位置 ,就向链表的next功能是一样的
    16 };
    17 node edge[2003];//存所有的边,因为是无向图,所以*2
    18 int cnt;//结构体的下标
    19 int n, s, t;//n 点数,s 起点,t止点
    20 int head[203];//和链表的头指针数组是一样的。只不过此处head[u]记录的为最后加入 edge 的且与u相连的边在 edge 中的位置,即下标
    21 
    22 void add(int u, int v, int w)//加边操作
    23 {
    24     edge[cnt].v = v;
    25     edge[cnt].w = w;
    26     edge[cnt].next = head[u];//获得下一个结构体的位置
    27     head[u] = cnt++;//记录头指针的下标
    28 }
    29 
    30 void dijkstra()
    31 {
    32     int dis[203];//最短路径数组
    33     int i, v;//v保存从队列中取出的数的第二个数  也就是顶点的编号
    34     priority_queue<P,vector<P>,greater<P> >que;//优先队列 从小到大
    35     node e;//保存边的信息,为了书写方便
    36     P p;//保存从队列取出的数值
    37 
    38     fill(dis,dis+n,MAX);//初始化,都为无穷大
    39     dis[s] = 0;//s—>s  距离为0
    40     que.push(P(0,s));//放入距离 为0   点为s
    41     while(!que.empty()){
    42         p = que.top();//取出队列中最短距离最小的对组
    43         que.pop();//删除
    44         v = p.second;//获得最短距离最小的顶点编号
    45         if(dis[v] < p.first)//若取出的不是最短距离
    46             continue;//则进行下一次循环
    47         for(i=head[v];i!=-1;i=edge[i].next)//对与此点相连的所有的点进行遍历
    48         {
    49             e = edge[i];//为了书写的方便。
    50             if(dis[e.v]>dis[v]+e.w){//进行松弛
    51                 dis[e.v]=dis[v]+e.w;//松弛成功
    52                 que.push(P(dis[e.v],e.v));//讲找到的松弛成功的距离 和顶点放入队列
    53             }
    54         }
    55     }
    56     printf("%d
    ",dis[t]==MAX?-1:dis[t]);//输出结果
    57 }
    58 
    59 int main()
    60 {
    61     int m, u, v, w;
    62 
    63     while(scanf("%d %d",&n,&m)==2){//获取点数  边数
    64         cnt = 0;//结构体下标从0开始
    65         memset(head,-1,sizeof(head));//初始化head[N]数组
    66         while(m--){
    67             scanf("%d %d %d",&u,&v,&w);//获取u,v,w(u,v)
    68             add(u,v,w);//加边
    69             add(v,u,w);//加边
    70         }
    71         scanf("%d %d",&s,&t);//获取起止点
    72         dijkstra();
    73     }
    74     return 0;
    75 }
    优先队列dijksta

    Dijkstra算法在运行过程中维持的关键信息是一组结点集合S。从源结点s 到该集合中每个结点之间的最短路径都已经被找到。算法重复从结点集V-S中选择最短路径估计最小的结点u,讲u加入到    集合S,然后对所有从u发出的边进行松弛。

    Dijkstra 算法如下://这个描述使用的最小优先队列Q来保存结点集合,每个结点的关键值为其d值。

        1   对图的建立和处理,dis[N]数组的初始化等等操作

        2    S = ∅

        3    Q = G.V

        4  while Q ≠ ∅

        5    u = EXTRACT-MIN(Q)

        6    S = S ∪ {u}

        7     for each vertex v∈ G.Adj[u]

        8                relax(u,v,w)

        此算法在此分为二步 : 第二大步中又分为3小步

        1)   第1~3行 对dis[N]数组等的初始化,集合S 为∅,Q集合为G.V操作

        2)   第4~8行 ① 第4行 进行G.V次操作

               ② 第5~行 从Q中找到一个点,这个点是Q中所有的点   s—>某点  最小的最短路径的点,并将此点加入S集合

               ③ 第7~8行  进行松弛操作,用此点来更新其他路径的距离。

    该算法适合于权值不为负数的有向图的单源最短路径。

    why?

    because :  

        0 3 4

               3 0 -2

        4 -2 0

     求d{1,2} 根据dirkstra实际就是贪心算法,每次找到都是最短路径, d{1,2} =3,但是因为有负权,d{1,2}=2;

    so 命题不成立#

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<iostream>
     4 #include<queue>
     5 #define MAX 99999999
     6 using namespace std;
     7 
     8 
     9 struct node
    10 {
    11     int u,v,w;  
    12 }; ///边的数值 
    13 
    14 node edge[2003];
    15 int n,m,s,t;
    16 
    17 void bellman_ford()
    18 {
    19     int i,j;
    20     bool flag;//用于优化的 
    21     int dis[203];//保存最短路径
    22     //初始化
    23     fill(dis,dis+n,MAX); 
    24     dis[s]=0;//源点初始化为0 
    25     m=m<<1;
    26     for(i=1;i<n;i++)
    27     {
    28         flag=false;//刚刚标记为假 
    29         for(j=0;j<m;j++)//
    30         {
    31             if(dis[edge[j].u]>dis[edge[j].v]+edge[j].w){
    32                 dis[edge[j].u]=dis[edge[j].v]+edge[j].w;
    33                 flag=true;//若松弛成功则标记为真 
    34             }
    35          } 
    36          if(!flag)//若所有边i的循环中没有松弛成功的 
    37          break;//推出循环 
    38     }
    39     printf("%d
    ",dis[t]==MAX?-1:dis[t]); // 输出结果 
    40     
    41 }
    42 
    43 int main()
    44 {
    45     int i;
    46     while(~scanf("%d%d",&n,&m))
    47     {
    48         for(i=0;i<m;i++)
    49         {
    50             scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
    51             edge[i+m].u=edge[i].v;//因为无向图所以u->v和v->u一样的
    52             edge[i+m].v=edge[i].u;//so
    53             edge[i+m].w=edge[i].w;//so 
    54         }
    55         scanf("%d%d",&s,&t);
    56         bellman_ford();
    57     }
    58     return 0;
    59 }
    View Code

    Bellman-Ford算法 :

         bellman-ford 算法解决的是一般情况下的单源最短路径问题,其边可以为负值。bellman-ford算法可以判断图是否存在负环,若存在负环会返回一个布尔值。当然在没有负环存在的    情况下会返回所求的最短路径的值。

        bellman-ford() :算法如下

        1   图的初始化等操作

        2  for i = 1 to |G.V| - 1   //  |G.V|  为图 G的点的总数

        3    for each edge(u,v)∈G.E   //G.E 为图 G 的边

        4           relax(u,v,w) 也就是if  v.d>u.d+w(u,v)  , v.d = u.d+w(u,v);

        5  for each edge(u,v)∈G.E

        6     if v.d>u.d+w(u,v)  //v.d为出发源点到结点v的最短路径的估计值  u.d亦如此  w(u,v) 为u结点到v结点的权重值(通俗点就是u—>v的花费)。

        7      return false;

        8  return true

        此算法分为3步:

        1)  第1行对图进行初始化,初始化dis[N] = +∞,dis[s] = 0;

         2)  第2~4行为求最短路的过程,是对图的每一条边进行|V|-1次松弛操作,求取最短路径。

         3) 第5~8行为对每条边进行|V|-1次松弛后,检查是否存在负环并返回相应的布尔值,因为进行|V|-1次松弛后若没有负环则v.d的值确定不变,若有负环则会继续进行松弛操作,因为一个数+负数是一定比它本身要小的。

        此算法的 时间复杂度为O(VE)。

    路径还原

     1 int per[503]; 
     2 memset(per,-1,sizeof(per)); 
     3 for(i=0;i<n;i++)
     4 {
     5     dis[i]=G[s][i];
     6     per[i]=s;
     7  } //初始化; 
     8  
     9  //k为找到当前最短路的点 
    10  if(!vis[j]&&dis[j]>dis[k]+G[k][j])
    11  {
    12      dis[j]=dis[k]+G[k][j];
    13      per[j]=k;//当前前驱为k 
    14  }
    15  
    16  //输出路径;
    17  int p;//记录前驱 
    18  for(i=0;i<n;i++)
    19  {
    20      if(per[i]==-1)//没有前驱 
    21          printf("%d
    ",i);
    22      else
    23      {
    24          p=per[i];
    25          while(p>0)
    26          {
    27              printf("%d ",p);
    28              p=per[p];
    29           } 
    30           printf("0
    ");
    31      }
    32   } 
    Dijkstra路径还原
  • 相关阅读:
    Floyd判圈算法 Floyd Cycle Detection Algorithm
    最优化问题 Optimization Problems & 动态规划 Dynamic Programming
    自平衡二叉搜索树
    树 & 二叉树
    数根
    二叉搜索树BST
    K-Means & Sequential Leader Clustering
    KMP算法
    递归问题的时间复杂度分析
    人工神经网络 Artificial Neural Network
  • 原文地址:https://www.cnblogs.com/WDKER/p/5161380.html
Copyright © 2020-2023  润新知