码迷,mamicode.com
首页 > 编程语言 > 详细

我的口水话:最小生成树、Prim、Kruskal算法是什么?

时间:2017-10-06 11:34:56      阅读:240      评论:0      收藏:0      [点我收藏+]

标签:社会主义   dwr   ns3   atq   baidu   jid   gdk   sae   lap   

#PS:这是我好几年前写的Blog,一直没发出来,所以和我现在的风格不大一样,but现在修改了一部分,还不错~~#

 今天,我们非常愉快地学习 如何去找“图”的“最小生成树”——Prim与Kruskal算法

什么叫做生成树?

简单的来说,我先给大家讲一个故事……

故事背景:

有一天,God•MTC告诉All around the world的老百姓,他要挑一个孩子将来当Cupid。怎么个挑法呢?他给所 有的孩子发了一粒花籽,看谁拿这粒花籽种出最美丽的Rose来。God•MTC还非常心血来潮地画了花的设计图:

技术分享很明显,God•MTC的小学美术肯定是数学老师教的,所以他在图上施了魔法,还要求孩子们种出来的Rose一定要像图上的一样“美丽”,不然就把他们统统扔进二次元 (qwq) ……然而,种子是炒熟了的,这显然是God•MTC的一场阴谋(哦天,暴君还没被推翻真是奇迹 !(À_Á)!)。

God•MTC还恐吓孩子们:“这颗种子是非常珍贵的!开出来的花都是金做的!” 导致孩子们不得不在通货膨胀的时代用昂贵的金子去做一朵假花(垄断资本主义的腐臭味儿……)。富家子女当然随随便便能拿到足够的金子;但是满腔热血、勤俭节约,践行社会主义核心价值观,自觉传承中华民族优秀传统文化的中华儿女,肯定是希望用最少的金子去做出Rose的样子来。God•MTC的手下大义凛然地分享了一个秘密:God•MTC有高度的近视,所以只要把设计图上花朵的端点连起来,God•MTC就看不出来的。//%% MTCheng all rights reserved %%// 

生成树:

于是,为了保证连通图里每个顶点都能够被连接上,并且能够最节省花边数量的“设计图”,就叫做“生成树”。

(想一想,连接两个顶点最少需要一条花边,三个顶点最少需要两条花边,那么连接n个呢——当然是(n-1)条花边啦)我大天朝子民的想象力是极为丰富的:

技术分享 技术分享 技术分享 技术分享

这些都是“合法的”Rose。。。

想想看,有n个顶点,如果做得出k>n-1条花边,那么我肯定可以完成玫瑰的形状了,

我们发现,每一个顶点到另一个顶点,有且只有一条路径(由花边组成的哟),没有“环”/“回路”;也就是说,不转身,就永远回不到起点。(文艺范有木有啊啊啊啊 [emoji]~)

最小生成树:

然而现实很残酷,大天朝子民在实践中探索,在劳动中突破,发现制作God‘s Rose的每一条边要用的成本是不一样滴……凝聚了中国古人智慧的“Rose资料”终于在2016年7月20日出炉啦(~^.^~):

 技术分享

(单位:元)

 很幸运,我也分到了一颗种子;很不幸,我是个口袋没有money的单身狗(’ ï _ ï `)。啊啊啊啊啊——【天崩地裂ing~~】——等等!

“我的大脑还能思维。”——霍金

 为了尽量节省我的pocket money,以至于我既当上了Cupid,也可以有足够经费去开车,我决定寻找所有设计方案中花费最小的那一种。在所有生成树中,边权总和最小的生成树,这就是“最小生成树”!!!

我跑去求程序猿小A。“啊~给你Prim算法Kruskal算法吧!我全家都用它!//%% MTCheng all rights reserved %%//

//=====================^_^=顽皮的分割线=^_^===================\\

 Prim算法:

假设我要从花心(即点1)开始制作这朵玫瑰,老司机会对我说:

“假设G=(V,E)是一个具有n个顶点的带权连通无向图,T=(U,TE)是G的最小生成树,其中U是T的顶点集,TE是T的边集,则由G构造最小生成树T的步骤如下:

  (1) 初始化U={v0}。v0到其他顶点的所有边为候选边;

  (2) 重复以下步骤n-1次,使得其他n-1个顶点被加入到U中:

          ① 从候选边中挑选权值最小的边输出,设该边在V-U中的顶点是v,将v加入U中,删除和v关联的边;

          ② 考察当前V-U中的所有顶点vi,修改候选边:若(v,vi)的权值小于原来和vi关联的候选边,则用(v,vi)取代后者作为候选边。”

 是不是太枯燥了……?

//%% MTCheng all rights reserved %%//

首先,我们先把花心用金色颜料涂上(这个不费钱),那么,为了保持玫瑰制作时不会受地心引力而分裂,我们决定下一步先看看那些是 “一头涂好金色颜料,一头没有碰过” 的花边:

  1.emm~~需要观察的边大约被我连接起来了。为了让我的预算一定是最小的,我决定找这些花边中,制作费最便宜的那条,即连接1-9的cost me 9438 元的那条花边,并且把边上的点也一起涂成金黄色,已经换作用金子铺的了。

  2.好吧,按一下F5刷新连接,看看现在有哪些是一头涂好金色颜料,一头没有碰过” 的花边//%% MTCheng all rights reserved %%//,再找到一条制作费最便宜的,即1-10 cost me 11438 元的那条花边,再把需要的东西涂色、填充。天啦,又花费了我大把的金子了啊(,º_º,)……

  3~n .重复与1或2相同的做法,也就是说,会不停地有花边因为我们新涂色了几个点而进入视野,也不停地有花边因为连接了两个金色点而被刷掉……

1.技术分享 2.技术分享 3.技术分享

 

void Prim(int m[][MAXV],int nv,int begin)
{
    int closest[MAXV];//c[i]=k记录i在最小生成树中的前驱结点是k
    int lowcost[MAXV];//l[i]=v记录了从节点i到已知树上任何一个节点的最小距离为v 
    int i,j,min,k;
    for(i=0;i<nv;i++)//初始化为树中存在begin节点的状态 
    {
        closest[i]=begin;
        lowcost[i]=m[begin][i];//这棵树就是只已知begin嘛~~ 
    }
    for(i=1;i<nv;i++)
    {
        min=INF;
        for(j=0;j<nv;j++)
        {
            if(lowcost[j]!=0&&lowcost[j]<min)//寻找出所有节点中非树的节点与已知树的最短距离 
            //lowcost[j]!=0证明此节点非树节点,lowcost[j]<min则表明当前j离树最近 
            {
                min=lowcost[j];
                k=j;//来个k作为替身吧 
            }
        }
        //输出边咯~(ò. ó)~//######################################################
        printf("Prim %d -> %d = %d\n",closest[k]+1,k+1,m[closest[k]][k]);
        
        lowcost[k]=0;//表明k已经在树上了 
        for(j=0;j<nv;j++) 
        {
            if(m[k][j]!=0&&m[k][j]<lowcost[j])
            /*m[k][j]!=0说明不是无用点(k==j) 
            m[k][j]<lowcost[j]说明在之前的节点Finish以下工作以后, 
            树上新的k节点比之前的节点 离节点j都要近 
            */
            {
                lowcost[j]=m[k][j];
                closest[j]=k;
            }
        }
    }
    
    return ; 
}
//%% MTCheng all rights reserved %%//
int main() { int nv;//the number of Vertexes int ne;//the number of Edges int m[MAXV][MAXV]; int i,j; int x,y,w; //Read in the graph scanf("%d%d",&nv,&ne); for(i=0;i<nv;i++) m[i][i]=0; for(i=0;i<ne;i++) { scanf("%d%d%d",&x,&y,&w); m[x][y]=w; m[y][x]=w; } //Do ~ ~ Prim(m,nv,0); }

 

 To be Continue~~

 

技术分享
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #define INF 32767
  5 #define MAXV 20
  6 typedef struct edge
  7 {
  8     int from,to;
  9     int weight;
 10 }sE;
 11 
 12 void print(int m[][MAXV])
 13 {
 14     int i,j;
 15     for(i=0;i<8;i++)
 16     {
 17         for(j=0;j<8;j++)
 18         {
 19             printf("%d-%d %d    ",i,j,m[i][j]);
 20         }
 21         printf("\n");
 22     }
 23     return ;
 24 }
 25 int cmp(const void *a,const void *b)
 26 {
 27     sE c=*(sE *)a;
 28     sE d=*(sE *)b;
 29     if(c.weight<d.weight)return -1;
 30     else if(c.weight==d.weight)return 0;
 31     else return 1;
 32 }
 33 int findx(int *fx,int x)
 34 {
 35     while(fx[x]!=x)
 36         x=fx[x];
 37     return x;
 38 }
 39 void Kruskal(int m[][MAXV],int nv,int ne,int begin)
 40 {
 41     sE e[ne];
 42     int fx[nv];
 43     int lene=0;
 44     int i,j;
 45     int r1,r2;
 46     for(i=0;i<nv;i++)
 47     {
 48         fx[i]=i;//顺便初始化并查集 
 49         for(j=i;j<nv;j++)
 50         {
 51             if(m[i][j]!=INF&&m[i][j]!=0)//将邻接矩阵转化成边 
 52             {
 53                 e[lene].from=i;
 54                 e[lene].to=j;
 55                 e[lene].weight=m[i][j];
 56                 ++lene;
 57             }
 58         }
 59     }
 60     qsort(e,ne,sizeof(e[0]),cmp);
 61     
 62     j=0;
 63     for(i=0;j<nv&&i<ne;i++)
 64     {
 65         r1=findx(fx,e[i].from);
 66         r2=findx(fx,e[i].to);
 67         if(r1!=r2)//此边可以使用 
 68         {
 69             //输出边咯~(ò^ ó)~//######################################################
 70             printf("Kruskal %d -> %d = %d\n",e[i].from+1,e[i].to+1,e[i].weight);
 71             fx[r1]=r2;
 72             j++;
 73         }
 74         //否则若加入此边 则出现回路 
 75     }
 76     return ;
 77 }
 78 void Prim(int m[][MAXV],int nv,int begin)
 79 {
 80     int closest[MAXV];//c[i]=k记录i在最小生成树中的前驱结点是k
 81     int lowcost[MAXV];//l[i]=v记录了从节点i到已知树上任何一个节点的最小距离为v 
 82     int i,j,min,k;
 83     for(i=0;i<nv;i++)//初始化为树中存在begin节点的状态 
 84     {
 85         closest[i]=begin;
 86         lowcost[i]=m[begin][i];//这棵树就是只已知begin嘛~~ 
 87     }
 88     for(i=1;i<nv;i++)
 89     {
 90         min=INF;
 91         for(j=0;j<nv;j++)
 92         {
 93             if(lowcost[j]!=0&&lowcost[j]<min)//寻找出所有节点中非树的节点与已知树的最短距离 
 94             //lowcost[j]!=0证明此节点非树节点,lowcost[j]<min则表明当前j离树最近 
 95             {
 96                 min=lowcost[j];
 97                 k=j;//来个k作为替身吧 
 98             }
 99         }
100         //输出边咯~(ò. ó)~//######################################################
101         printf("Prim %d -> %d = %d\n",closest[k]+1,k+1,m[closest[k]][k]);
102         
103         lowcost[k]=0;//表明k已经在树上了 
104         for(j=0;j<nv;j++) 
105         {
106             if(m[k][j]!=0&&m[k][j]<lowcost[j])
107             /*m[k][j]!=0说明不是无用点(k==j) 
108             m[k][j]<lowcost[j]说明在之前的节点Finish以下工作以后, 
109             树上新的k节点比之前的节点 离节点j都要近 
110             */
111             {
112                 lowcost[j]=m[k][j];
113                 closest[j]=k;
114             }
115         }
116     }
117     
118     return ; 
119 }
120 int main()
121 {
122     int nv;//the number of Vertexes
123     int ne;//the number of Edges
124     int m[MAXV][MAXV];
125     int i,j;
126     int x,y,w;
127     scanf("%d%d",&nv,&ne);
128     for(i=0;i<nv;i++)
129         m[i][i]=0;
130     for(i=0;i<ne;i++)
131     {
132         scanf("%d%d%d",&x,&y,&w);
133         m[x][y]=w;
134         m[y][x]=w;
135     }
136     Prim(m,nv,0);
137     Kruskal(m,nv,ne,0);
138 } 
神奇的呆码

 

我的口水话:最小生成树、Prim、Kruskal算法是什么?

标签:社会主义   dwr   ns3   atq   baidu   jid   gdk   sae   lap   

原文地址:http://www.cnblogs.com/MicoTimlesCheng/p/5689574.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!