Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2793244
  • 博文数量: 471
  • 博客积分: 7081
  • 博客等级: 少将
  • 技术积分: 5369
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-04 21:55
文章分类

全部博文(471)

文章存档

2014年(90)

2013年(69)

2012年(312)

分类: C/C++

2012-05-09 13:19:20

单源最短路径问题[Dijkstra实现]

一、问题

 带权有向图G(E,V), 找出从给定源顶点s到其它顶点v的权最小路径。

“最短路径” = 最小权


其基本思想是,设置顶点集合S并不断地作贪心选择来扩充这个集合。一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。

初始时,S中仅含有源。设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径并用数组dist记录当前每个顶点所对应的最短特殊路径长度。Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist作必要的修改。一旦S包含了所有V中顶点,dist就记录了从源到所有其它顶点之间的最短路径长度。

效率方面,因为每次都要从都要从Dn里找最小Dv,所以时间效率是V2的。如果是稠密图,基本上程序比较优了。关于稀疏图用优先队列,二叉堆优化神马的,以后再看...如果需要每个顶点作为源的所有最短路,在稠密图下,Floyd比循环n次Dijkstra稍快,且简洁。

二、问题求解:

求1到5的最短路径值?

三、执行过程:

 

如果大家对这个问题的要求还不是很明白的话那么我再带着大家走一遍:

第一次:从1-->2:10 此时从1-->3没有路径所有是无穷大  1-->4:30  1-->5:100那么我们发现这一组组最小的是10也就是2这一点,所以我们再把2这一点加到集合里面来,那么2这一点就可以当作一个桥来用,

第二次:此时我们再从1到3就可以通过1-->2-->3:60其他的1-->4:30 ,1-->5:100 可以发现此时最小的应该是4,所以我们再把4这一点加入到这个集合里面来,如此重复的去做这些事情,

到最后可以发现1到5的最短路径应该是60(1-->4-->3-->5)

一旦S包含了所有V中顶点,dist就记录了从源到所有其它顶点之间的最短路径长度。 

  1. int dijkstra(int s,int t)
  2. {

  3.        初始化S={空集}

  4.        d[s] = 0; 其余d值为正无穷大

  5.        while (NOT t in S)

  6.        {

  7.               取出不在S中的最小的d[i];

  8.               for (所有不在S中且与i相邻的点j)

  9.                 if (d[j] > d[i] + cost[i][j])
  10.                 d[j] = d[i] + cost[i][j]; ( “松弛”操作” )

  11.               S = S + {i}; //把i点添加到集合S里

  12.        }

  13.        return d[t];

  14. }

  1. #include <iostream>

  2. using namespace std;

  3. #define MAX 9999999

  4. #define LEN 210

  5. int map[LEN][LEN]; //某点到某点两点间的的距离
  6. int dist[LEN]; //记录当前点到源点的最短路径长度
  7. int mark[LEN]; //加入进来的点的集合

  8.  

  9.     //初始化map为正无穷大
  10.     void init()
  11.     {
  12.        int i,j;
  13.        for(i=0;i<LEN;i++)
  14.      {
  15.               for(j=0;j<LEN;j++)
  16.              {
  17.                      map[i][j]=MAX;
  18.               }
  19.        }
  20.     }

  21.     //n:多少条路 start:起始点
  22.     //dist[i],最后存储着start 到i点的最短距离
  23.     void myDijstra(int n,int start)
  24.     {
  25.        int i,j,min,pos;
  26.        for(i=1;i<=n;i++)
  27.      {
  28.             mark[i]=0;//没有点加入
  29.             dist[i]=map[start][i];//把start 附近点 dis[]初始化
  30.        }

  31.        mark[start]=1;//把起始点加进来
  32.        dist[start]=0;

  33.        for(i=1;i<=n;i++)
  34.      {

  35.               min=MAX;
  36.               for(j=1;j<=n;j++)
  37.              {
  38.                      if(!mark[j] && dist[j]<min)
  39.                      { //取出不在mark里的最小的dist[i]
  40.                          min=dist[j];
  41.                          pos=j;//标记
  42.                      }

  43.               }
  44.                 
  45.              if(min==MAX)//已经不能通了
  46.                   break;

  47.                 mark[pos]=1;//把K加进来

  48.               //做松弛操作
  49.               for(j=1;j<=n;j++)
  50.              {
  51.                     if(!mark[j] && dist[j]>dist[pos]+map[pos][j])//start->j or start->pos,pos->j
  52.                     {
  53.                        dist[j]=dist[pos]+map[pos][j];//这步跟prim算法有点不同
  54.                     }
  55.               }
  56.         }
  57.     }

  58.  

  59. int main(){

  60.        int i,n,line;

  61.        int a,b,d;
  62.        scanf("%d%d",&n,&line);//输入点和
  63.        init();
  64.        for(i=0;i<line;i++)
  65.      {

  66.            scanf("%d%d%d",&a,&b,&d); //输入各边的权值
  67.             // 这个判断是防止重边的,因为也许会对同一条路给出两个值,这时就保存较小的值 
                if(map[a][b]>d)
  68.             {
  69.             map[a][b]=map[b][a]=d;
  70.             }
  71.        }

  72.        myDijstra(n,1);//调用方法(点数,起始点)

  73.        //输出1到5的最短路径

  74.        cout<<dist[5]<<endl;

  75.        return 0;

  76. }

  77. /*
  78. 5 7
  79. 1 2 10
  80. 1 4 30
  81. 1 5 100
  82. 2 3 50
  83. 3 5 10
  84. 4 3 20
  85. 4 5 60
  86. 60
  87. */


参考

http://blog.csdn.net/kootain/article/details/6683646
http://blog.csdn.net/jiahui524/article/details/6636913
阅读(8798) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~