prim 与dijkstra的异同 POJ 2485 Highways

题意:一个地方F,没有Highways,交通不便,要建 Highways,每个Highway连接两个城镇,所有的Highways都是直线的。

样例输入意思:

T  案例数

N 城镇数

下面N行N列,以矩阵的形式

     v1   v2    v3

v1  0    990  692 

v2 990   0    179

v3 692  179   0

样例输出意思:

明显路径是 v1-v3-v2,路径中最大的数是692,输出的就是它

思路:

最小生成树问题,一般用prim,kuskal算法 。prim算法是以顶点来扩展的。把每次找到的顶点加入顶点集U,然后下次再找与U相邻且最小的顶点,加入U,依次,得到最小生成树。

参考:http://en.wikipedia.org/wiki/Prim's_algorithm

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define M  502  
  4. #define INF 99999  
  5. int prim[M][M];  
  6. int visit[M];  
  7. int Len[M];  //Len[i] 记录顶点集U到i的最短距离,注意区别dijkstra中的dis[i]  
  8. int n;  
  9. int ans;  
  10. int  prim_solve(int xx)  
  11. {  
  12.     int minx;  
  13.     int i,j,k;  
  14.     memset(visit,0,sizeof(visit));  //开始时都未访问  
  15.     ans = -1;  
  16.     for (i = 1; i <= n; i++)  
  17.     {  
  18.         Len[i] = prim[xx][i];  
  19.     }  
  20.     Len[xx] = 0;  
  21.     visit[xx] = 1;  //此时U中只有起点xx  
  22.     for(i = 1; i< n; i++)  // 注意:不能=,因为xx起点已经访问过,所以只需再访问n-1个  
  23.     {  
  24.         minx = INF;  
  25.         for(j = 1; j <= n; j++ )   //很像dijkstra中的吧,但注意:这里的Len[i]与dijkstra中dis[i]意义相当不同  
  26.         {                          //这里找的是:与顶点集U相邻的距离最小值  
  27.             if ( !visit[j]  && Len[j] < minx)  
  28.             {  
  29.                 minx = Len[j];  
  30.                 k = j;  
  31.             }  
  32.         }  
  33.         visit[k] = 1;   //找到,加入U  
  34.         if (ans < minx)   //保存最短路径中最大的一条边,比如样例中692  
  35.         {  
  36.             ans = minx;  
  37.         }  
  38.         //i=1时,U中只有起点xx和新加入的k,Len[j]与prim[j]比较:就是比较xx到j的距离和新加入U的k顶点到j的距离  
  39.         //之后,Len[j]就是U到j的最短距离啦,这样把U中所有顶点看成一个,Len[j]就是U到j(V-U中任意一个)的最短距离  
  40.         //以此类推,i>1 时,每次都把原来的顶点集U到j的距离和新加入的k到j的距离比较,这样得到了新U到j的最短距离  
  41.         //从而,就得到了新U到V-U中任一顶点的距离,保存在 Len中  
  42.         for (j = 1; j <= n; j++)     
  43.         {  
  44.             if ( !visit[j] && Len[j] > prim[k][j])  
  45.             {  
  46.                 Len[j] = prim[k][j];  
  47.             }  
  48.         }  
  49.     }  
  50.     return ans;  
  51. }  
  52. int main()  
  53. {  
  54.     int T;  
  55.     int i,j;  
  56.     scanf("%d",&T);  
  57.     while(T--)  
  58.     {  
  59.         memset(prim,0,sizeof(prim));  
  60.         scanf("%d",&n);  
  61.         for(i = 1; i <= n; i++)  
  62.             for(j = 1; j <= n; j ++)  
  63.             {  
  64.                 scanf("%d",&prim[i][j]);  
  65.             }  
  66.         printf("%d\n",prim_solve(1)); //以第一个顶点开始,也可以是其他,无所谓,改成2。。。。一样  
  67.     }  
  68.     return 0;  
  69. }  

你会觉得prim代码实现是来很像dijkstra,参考http://blog.csdn.net/bill_ming/article/details/7578037

prim是计算最小生成树的算法,dijkstra是计算最短路径的算法。

他们都是从几何V-U中不断选出权值最小的顶点加入U,那他们是否可以互用吗?

必然不可以嘛!

他们的不同之处是:

prim是把U中的点看成一个整体,每次寻找V-U中和U距离最小的顶点加入U。而dijkstra是相对于源点V0而言的,每次寻找的是V-U中里V0最近的顶点。

所以:

dijkstra中dis[i]记录的是V0到i的最短距离。

用这样的for循环来更新dis,结果dis[j]是比较源点V0直接到j   和   源点V0经过k到j的最短距离所得结果

 for(j = 1; j <= n; j++)   
        {
            if ( !visit[j] && dis[j] > dis[k] + map[k][j])
            {
                dis[j] = dis[k] + map[k][j];
            }
        }

而prim中Len[i]记录的是顶点集U(看成整体)到i的最短距离

用这样的for循环来更新顶点集U到V-U中任一顶点的距离,比较的是加入k之前顶点集到j的距离  和 k 到j的距离 。

得到的就是包含k的新顶点集U到j的最短距离。   (感谢罗康琦大牛!!!)

//i=1时,U中只有起点xx和新加入的k,Len[j]与prim[j]比较:就是比较xx到j的距离和新加入U的k顶点到j的距离
        //之后,Len[j]就是U到j的最短距离啦,这样把U中所有顶点看成一个,Len[j]就是U到j(V-U中任意一个)的最短距离
        //以此类推,i>1 时,每次都把原来的顶点集U到j的距离和新加入的k到j的距离比较,这样得到了新U到j的最短距离
        //从而,就得到了新U到V-U中任一顶点的距离,保存在 Len中
        for (j = 1; j <= n; j++)   
        {
            if ( !visit[j] && Len[j] > prim[k][j])
            {
                Len[j] = prim[k][j];
            }
        }

他们的区别就知道了。所以不能互用啊,prim还是乖乖算最小生成树吧,dijkstra还是继续管计算最短路径吧

举个例子就知道他们不能乱来了:

有四个顶点(v0, v1, v2, v3)和四条边且边值定义为(v0, v1)=20, (v0, v2)=10, (v1, v3)=2, (v3, v2)=15的图,用Prim算法得到的最小生成树中v0v1是不直接相连的,也就是在最小生成树中v0v1的距离是v0->v2->v3->v1的距离是27,而用Dijkstra算法得到的v0v1的距离是20,也就是二者直接连线的长度。


SO。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值