1. 程式人生 > >克魯斯卡爾求最小生成樹

克魯斯卡爾求最小生成樹

角度 ems 不理解 += 重復 highlight 賦值 truct bool

處理何種問題:求解最小生成樹,適合點多邊少的無向圖。(以證明,放心用)

性能:時間復雜度為O(e*loge),e為邊的個數。

原理:貪心策略

實現步驟

<1>設一個有n個頂點的聯通網絡為G(V,E),最初先構造一個只有n個頂點,沒有邊的非連通圖T={V,空},圖中的每一個頂點自成一個連通分量。

<2>在E中選擇一條具有最小權值的邊時,若該邊的兩個頂點落在不同的連通分量上,則將此邊加入到T中;否則,即這條邊的兩個頂點屬於同一個連通分量,將此邊舍去(此後永不選用這條邊),重新選擇一條權值最小的邊。

<3>如此重復下去,直至選了(n-1)條有效邊,或者所有的頂點都在同一連通分量上為止。

備註:在此之前,本人對於網上給出的對於克魯斯卡爾的證明並不理解,只覺得這種貪心策略有Bug,這次換一個角度去理解這個算法:<1>一個孤立的點V要想連入最後的那個生成樹中,只需要找到在邊的中找連接V,且權值最小的那條邊即可,則可證明,權值最小的那條邊必然在最後的最小生成樹中;<2>對於已經連線的兩個孤立點,可看作一個點,他們共享互相邊的性質。<3>一顆生成樹有且僅有n-1條邊。根據以上三點,即可推出克魯斯卡爾的正確性。只是在實現方式上有些難以理解。

輸入樣例解釋

4 6//頂點的個數,邊的個數

1 2 1//一條邊的兩個頂點和該邊上的權值

1 3 4

1 4 1

2 3 3

2 4 2

3 4 5

輸出樣例解釋

5//該無向圖的最小生成樹

代碼

#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;

const int MaxN=510;///最大頂點的數
const int MaxM=251000;///最大邊數
int F[MaxN];///並查集

struct Edge
{
    int u,v,w;
};
Edge edge[MaxM];///儲存邊的信信息,包括起點/終點/權值

int tal;///邊數,加邊前賦值為0

void addedge(int u,int v,int w)
{
    edge[tal].u=u;
    edge[tal].v=v;
    edge[tal].w=w;
    ++tal;
}

bool cmp(Edge a,Edge b)///排序函數,邊按照權值從小到大排序
{
    return a.w<b.w;
}

int Find(int x)
{
    if(F[x]==-1)
        return x;
    else
        return F[x]=Find(F[x]);
}

int Kruskal(int n)///傳入點數,返回最小生成樹的權值,如果不連通則返回-1
{
    memset(F,-1,sizeof(F));
    sort(edge,edge+tal,cmp);

    int cnt=0;///計算加入的邊數
    int ans=0;
    int u,v,w,t1,t2;

    for(int i=0;i<tal;++i)
    {
        u=edge[i].u;
        v=edge[i].v;
        w=edge[i].w;

        t1=Find(u);
        t2=Find(v);

        if(t1!=t2)
        {
            ans+=w;
            F[t1]=t2;
            cnt++;
        }
        if(cnt==n-1)
            break;
    }
    if(cnt<n-1)
        return -1;///不連通
    else
        return ans;
}


int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        tal=0;///必須是0
        int u,v,w;
        for(int i=0;i<m;++i)
        {
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        printf("%d\n",Kruskal(n));
    }
    return 0;
}
/**
4 6
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
5
*/

  

克魯斯卡爾求最小生成樹