關於最小生成樹


最小生成樹

\(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大佬的博客排序

相關文章
相關標籤/搜索