集训8.16最小生成树讲解

来源:互联网 发布:长篇小说投稿 知乎 编辑:程序博客网 时间:2024/06/05 04:28

最小生成树(mst),无向图,什么是最小生成树,最小生成树就是包括所有顶点(不一定成环)且权值之和最小,涉及到这类题目,有时候题目保证一定有最小生成树,没有题目会说没有输出-1等等,怎么判断有没有下面讲解过程中提到,与最小生成树相反的是最大生成树,有时候题目保证一定有最大生成树,没有题目会说没有输出-1等等,怎么判断有没有下面讲解过程中提到。

最小生成树的算法有两个一个是prim算法另一个是kruskal算法

prim算法

算法描述:任意选出一个顶点作为初始顶点(我都是选第一个顶点),v是顶点的集合,一开始为空,以后陆续将顶点加入集合,全部顶点加入集合就得到最小生成树,

                  任选一个顶点放入树,初始化v和lowcost

                  在那些一个顶点在树里另一个端点不在树里的边中选一个边权最小的边,将此边和端点加入数,执行更新,重复上一步知道所有顶点都入树

实现代码:

void prim()

{

    memset(v,0,sizeof(v));

   v[1]=1;

    for(i=1;i<=n;i++)n节点个数

    lowcost[i]=edge[1][I];

   for(i=2;i<=n;i++)n节点个数

   {

         min=inf;(最大生成树时min=-inf)

         for(j=1;j<=n;j++)

           {

                if(!v[j]&&minn>lowcost[j])(求最大生成树时if(!v[j]&&minn<lowcost[j]){minn=lowcost[j];next=j;}

                 {

                        minn=lowcost[j];

                        next=j;

                 }

           }

                (

                      判断是否能构成最小生成树

                      if(minn==inf)

                      {cout<<-1<<endl;return ;}

                     判断是否能构成最大生成树

                     if(minn==-inf)

                    {cout<<-1<<endl; return ;}

              )

            sum+=minn;

           v[next]=1;

          for(j=1;j<=n;j++)

          {

                 if(!v[j]&&lowcost[j]>edge[next][j])(求最大生成树时if(!v[j]&&lowcost[j]<edge[next][j]) lowcost[j]=edge[next][j];)

                      lowcost[j]=edge[next][j];

           }

          }

           cout<<sum<<endl;

}

int main()

{

   若题目以矩阵的形式给出

  for(i=1;i<=n;i++)

  for(j=1;j)<=n;j++) edge[i][j]=

  若不是以矩阵的形式给出 需要初始化edge

for(i=1;i<=n;i++)n是节点个数

 for(j=1;j<=n;j++) edge[i][j]=inf;(求最大数时赋值为-inf)

初始化完之后输入是 edge[i][j]=edge[j][i]=

若有重边只需在初始化完之后输入时加个判断

if(edge[i][j]>c)选重边中最小的

edge[i][j]=edge[j][i]=c (求最大生成树时若有重边只需在初始化之后输入时加个判断 if(edge[i][j]<c)选重边中最大的 edge[i][j]=edge[j][i]=c)

prim();

}

kruscal算法:

算法描述:

               将边权由小到大排序(求最大生成树时只需将边权由大到小排序其余一样)

               初始化fat[x]=x;

               tot=0;

                  计数器 k=0;

                将边权排序

              if(他俩不在一个集合里)

              {合并  tot+=w(u,v);k++;}

             if(k=顶点个数-1) 树已生成

实现代码:

struct node

{

    int s,e,v;//起点终点权值

};

bool cmp(node a,node b)

{

    return a.v<b.v;

}

int find(int x)

{

     if(fat[x]!=x)

     fat[x]=find(fat[x]);

     return fat[x];

}

void nuionn(int x,int y)

{

      int fa=find(x);

      int fb=find(y);

      if(fa!=fb) fat[fa]=fb;

  }

int main()

{

   如果以矩阵的形式给出

    m=0;

    for(i=1;i<=n;I++)

    {

             for(j=1;j<=m;j++)

               {

                   cin>>x;

                   if(x!=0)

                    {

                       m++;

                     edge[m].s=I;

                      edge[m].e=j;

                       edge[m].v=x;

                     }

                }

     }

     如果不以矩阵的形式给出

     for(i=1;i<=m;i++)//m边数

     {

          cin>>a>>b>>c;

          edge[I].s=a;edge[I].e=b;edge[I].v=c;

     }

   for(i=1;i<=n;i++) fat[i]=i;n是顶点的个数

   sort(edge+1,edge+1+m,cmp);

    tot=0;k=0;

    for(i=1;i<=m;I++)m边数

    {

            if(find(edge[I].s)!=find(edge[I].e)

           {

                union(edge[I].s,edge[I].e);

                   tot+=edge[I].v;

                  k++;

            }

      }

     if(k==n-1) cout<<tot<<endl;

     else cout<<-1<<endl;//没生成树

}