zoukankan      html  css  js  c++  java
  • 最小生成树

      最小生成树是指对于一个带边权的图,要求删去一些边,使点之间仍相连且剩下的边权尽可能小。以下给出两种方法,都用到了贪心法:
        ①Prim:先建立两个集合S和T,分别表示已连接和未连接的点集。开始时把在S中加入任意一个点x,并在T中加入1到n所有除x以外的点。然后每次找出T中连接到S边权最小的一个点把它加入S,同时从T删除,直到T为空。主要程序段如下:

        int lowcost[MAXN],closest[MAXN];
        int prim(int v0)
        {
            int i,j,mindis,minone;
            int ans = 0;/*用来记录最小生成树的总长度*/
            /*各点距离初始化*/
            for(i = 0;i < n;i++)
            {
                lowcost[i] = cost[v0][i];
                closest[i] = v0;
            }
            for(i = 0;i < n-1;i++)
            {
                mindis = UPPERDIS;
                for(j = 0;j < n;j++)
                  if(lowcost[j] && mindis > lowcost[j])
                  {
                      mindis = lowcost[j];
                      minone = j;
                  }
                /*将找到的最近点加入最小生成树*/
                ans += lowcost[minone];
                lowcost[minone] = 0;
                /*修正其他点到最小生成树的距离*/
                for(j = 0;j < n;j++)
                  if(cost[minone][j] < lowcost[j])
                  {
                      lowcost[j] = cost[j][minone];
                      closest[j] = minone;
                  }
            }
            return ans;
        } 

        时间复杂度为O(n^2)
        ②Kruskal:这种方法很简单,也很高效。先把边从小到大排序,依次枚举边并判断其两点是否在同一连通块(即是否已相连),若没有就加入该边。朴素算法时间复杂度为O(n^2),可用并查集或斐波那契堆加速,以下给出并查集加速代码(网上找来的,莫名地长):

       #include <stdio.h>
        #include <stdlib.h>
        #define MAX 100
        /* 定义边(x,y),权为w */
        typedef struct{
         int x, y;
         int w;
        }edge; 
        edge e[MAX];
        /* rank[x]表示x的秩 */
        int rank[MAX];
        /* father[x]表示x的父节点 */
        int father[MAX];
        int sum;
     
        /* 比较函数,按权值(相同则按x坐标)非降序排序 */
        int cmp(const void *a, const void *b){
            if ((*(edge *)a).w == (*(edge *)b).w){
               return (*(edge *)a).x - (*(edge *)b).x;
             }        return (*(edge *)a).w - (*(edge *)b).w;
        }
     
        /* 初始化集合 */
        void Make_Set(int x){
         father[x] = x;
         rank[x] = 0;
        }
     
        /* 查找x元素所在的集合,回溯时压缩路径 */
        int Find_Set(int x){
            if (x != father[x]){            father[x] = Find_Set(father[x]);
             }
             return father[x];
        }
     
        /* 合并x,y所在的集合 */
        void Union(int x, int y, int w){
             if (x == y) return;
             /* 将秩较小的树连接到秩较大的树后 */
             if (rank[x] > rank[y]){             father[y] = x;
             }
             else{
                 if (rank[x] == rank[y]){
                     rank[y]++;
                  }
                 father[x] = y;        }
         sum += w;
        }
     
        /* 主函数 */
        int main(){
         int i, n;
         int x, y;
         char chx, chy;
     
         /* 读取边的数目 */
         scanf("%d", &n);
         getchar();
     
         /* 读取边信息并初始化集合 */
         for (i = 0; i < n; i++){
              scanf("%c %c %d", &chx, &chy, &e[i].w);
              getchar();
              e[i].x = chx - 'A';
              e[i].y = chy - 'A';
              Make_Set(i);
         }
     
         /* 将边排序 */
         qsort(e, n, sizeof(edge), cmp);
     
         sum = 0;
     
             for (i = 0; i < n; i++){
                  x = Find_Set(e[i].x);
                  y = Find_Set(e[i].y);
                  if (x != y){
                       printf("%c - %c : %d
    ", e[i].x + 'A', e[i].y + 'A', e[i].w);
                       Union(x, y, e[i].w);
                  }
             }
     
         printf("Total:%d
    ", sum);
         return 0; 
        }

         并查集加速后,时间复杂度为O(n*log(n))。

  • 相关阅读:
    桥接模式(Bridge)
    Python中文件操作
    Python中字符的编码与解码
    反转部分单向链表
    删除链表的中间节点和a/b处的节点
    链表中删除倒数第K个节点
    如何实现链表的逆序
    Python高阶函数及函数柯里化
    Python函数作用域
    Python函数参数与参数解构
  • 原文地址:https://www.cnblogs.com/Enceladus/p/4979104.html
Copyright © 2011-2022 走看看