关于最小生成树


最小生成树

\(By:Soroak\)算法

  • 定义:一个有 \(n\) 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的全部 \(n\) 个结点,而且有保持图连通的最少的边。最小生成树能够用 \(kruskal\) 算法或 \(Prim\) 算法求出。

Kruskal

  • 定义: \(Kruskal\) 是基于贪心的思想获得的。
    首先咱们把全部的边按照权值先从小到大排列,接着按照顺序选取每条边,若是这条边的两个端点不属于同一集合,那么就将它们合并,直到全部的点都属于同一个集合为止。看到这里,咱们不难想到另一个算法——并查集,说白了, \(Kruskal\) 算法就是基于并查集的贪心算法。函数

  • 时间复杂度: \(O(MlogM)\) \(M\) 是图中边的总数。
  • 基本思想: \(Kruskal\) 是以边为主导地位,始终选择当前可用的最小边权的边,每次选择边权最小的边链接的时候,要判断两个端点之间有没有联通。spa

代码以下:(感谢gyh大佬的“赞助”)code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

struct edge
{
    int u,v,w;//分存每一条边前,后坐标与权值 
}a[3000010];

int n,m,num;//存边的数量 
int pre[1000010];//存并查集中的祖先 

bool cmp(edge aa,edge bb)
{
    return aa.w<bb.w;
}//结构体sort排序必须自定义排序函数 

void add(int u,int v,int w)
{
    a[++num].u=u;
    a[num].v=v;
    a[num].w=w;
}

int find(int x)
{
    return pre[x]==x?x:pre[x]=find(pre[x]);
}

void join(int x,int y)//并集 
{
    int r1=find(x),r2=find(y);
    if(r1!=r2) 
    {
        pre[r1]=r2;
    }
}

signed main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) 
    {
        pre[i]=i;//重置先祖 
    }
    for(int j=1;j<=m;j++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);//输入各边权值 
        add(u,v,w);
    }
    sort(a+1,a+num+1,cmp);
    int sum=0;
    for(int i=1,tot=0;i<=num&&tot!=n;i++)
    {
        if(find(a[i].u)==find(a[i].v)) 
        {
            continue;
        }
        join(a[i].u,a[i].v);
        ++tot;
        sum+=a[i].w;
    }
    printf("%d",sum);//输出 
    return 0;
}

Prim

  • 定义: \(Prim\) 算法,图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的全部顶点且其全部边的权值之和亦为最小。
  • 大体思想: \(Prim\)算法,是从点的方面考虑构建一颗最小生成树,而 \(Kruskal\) 算法则是从边的方面考虑构建一颗最小生成树。
  • 流程:
    • 将一个图分为两个部分,一部分为点集U,另外一部分为点集V,U的初始集合为{V1},V的初始集合为{除V1以外的全部点}
    • 针对U开始寻找U中各节点所关联的边的权值最小的那个,而后将关联的点Vi加到U中,再把Vi从V中删除(注意:不能造成环!!!)
    • 递归执行步骤2,直到V中的集合为空
    • U中全部的点所构成的点就是这个图的最小生成树。

代码:blog

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
const int INF = 0x7ffff;//最大值 
const int MARX = 1e5+10;
using namespace std;
//=============================================================
struct edge
{
    int u,v,w,ne;//分存前点 后点 权重 
}e[MARX<<2];
struct p
{
    int num,diss;
    bool operator < (const p &a) const
      {
        return diss > a.diss;
      }
}tmp;
int head[MARX],dis[MARX];
bool f[MARX]; 
int num,n,m,s=1;
//=============================================================
void add(int u,int v,int w)//邻接表加入元素 
{
    e[++num].ne=head[u],head[u]=num;
    e[num].u=u,e[num].v=v,e[num].w=w;
}
void dj(int s)
{
    priority_queue <p> q;
    tmp.num=s,tmp.diss=0;q.push(tmp);
    for(int i=1;i<=n;i++) dis[i]=INF;//赋极值 
    dis[s]=0;//初始化 
    for(int i=0;i<n && (!q.empty());)
    {
      int top=q.top().num; q.pop();
      if(f[top]) continue;
      i++,f[top]=1;
      for(int j=head[top];j;j=e[j].ne)//找k点的临点,并进行比较 
        if(dis[e[j].v] > e[j].w && (!f[e[j].v]))
        {
          dis[e[j].v] = e[j].w;
          tmp.num=e[j].v , tmp.diss=dis[e[j].v];
          q.push(tmp);
        }
    }
}
//=============================================================
int main()
{
    scanf("%d%d",&n,&m);//输入
    for(int i=1;i<=m;i++)
    {
      int u,v,w;
      scanf("%d%d%d",&u,&v,&w);
      add(u,v,w);
      add(v,u,w);
    }
    dj(s);
    int sum=0;
    for(int i=1;i<=n;i++) sum+=dis[i];
    printf("%d",sum);
}

/*
//如题,此题为最小生成树prim模板
//再也不赘述 
#include<cstdio>
#include<algorithm>
using namespace std;
const int MARX=2147483646;
struct baka9
{
    int u,v,w,ne;
}a[401000];
int head[20010];
int minn[20010];
bool f[20010];
int n,m,ans,num;
void add(int x,int y,int z)
{
    a[++num].ne=head[x];
    a[num].u=x;
    a[num].v=y;
    a[num].w=z;
    head[x]=num;
}
bool prim();
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
      {
        int x,y,z; 
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
      }
    if( prim() )
      printf("%d",ans);
    else
      printf("orz");
}
bool prim()
{
    for(int i=2;i<=n;i++)
      minn[i]=MARX;
    for(int i=head[1];i;i=a[i].ne)
      minn[a[i].v]=min(minn[a[i].v],a[i].w);
    for(int i=1;i<n;i++)
      {
        int minnn=MARX,k=-1;
        for(int j=1;j<=n;j++)
          if(!f[j] && minn[j]<minnn)
            {
              minnn=minn[j];
              k=j;
            }
        if(k==-1) break;
        f[k]=1;
        for(int l=head[k];l;l=a[l].ne)
          {
            if(!f[a[l].v] && minn[a[l].v] > a[l].w)
              minn[a[l].v]=a[l].w;
          }
      }
    for(int i=1;i<=n;i++)
      {
        if(minn[i]==MARX)
          return 0;
        ans+=minn[i];
      }
    return 1;
}
*/

这里宣传一下gyh大佬的博客排序

相关文章
相关标签/搜索