最短路径 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\n",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\n",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\n",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\n",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\n");
31      }
32   } 
Dijkstra路径还原

 

posted on 2016-01-26 19:43  青春的梦想付诸行动  阅读(196)  评论(0编辑  收藏  举报

导航