1. 程式人生 > >MST (最小生成樹)

MST (最小生成樹)

我們有一個無向圖,然後要求生成一棵邊權之和最小的樹

首先,我們可以暴力,列舉每一條邊選不選,然後計算邊權和,更新答案,必定會TLE,這是顯然的;

那麼我們需要一種較為高效的演算法來解決這種問題,這時候,我們就可以學一下MST(最小生成樹)的Kruskal演算法了

這個演算法用到了一些貪心的思想,就是我們每次選當前待選的邊權最小的那條邊,如果這條邊符合性質,我們就把它加入到樹中,否則,我們換下一條邊,一直重複這個過程,知道我們加入了n-1條邊(n為節點數);

那麼顯然我們是要排序的,根據邊權大小由小到大排序,每次取出一條邊,判斷是否合法的方法是用並查集,如果兩個端點在一個集合中,說明這兩個點一定在此之前由一些更短的邊在連線其它的點的時候將這兩個點連入了一個集合,所以當前邊是沒必要選的,且不可以選,不然違反了樹的性質,有了環,如果兩個端點不在一個集合,我們就把這兩個點連入一個集合,處理完當前的邊後,繼續下一條邊,直至結束;

完整程式碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define II long long
#define I 223456
using namespace std;


struct node {
    II x,y;
    II flow;
}aa[I];


II n,m,ans;

II fa[I];


bool map(node a1,node a2)
{
    return a1.flow<a2.flow;
}


II find(II x)
{
    if
(x==fa[x]){ return fa[x]; } else{ return fa[x]=find(fa[x]); } } void join(II x,II y) { fa[x]=y; } int main() { scanf("%lld%lld",&n,&m); for(II i=1;i<=m;i++) { II x,y,z; scanf("%lld%lld%lld",&x,&y,&z); aa[i].x=x; aa[i].y=y; aa[i].flow=z; //選擇結構體存邊,保證不會出現亂的情況;
} sort(aa+1,aa+m+1,map); //貪心排序; for(II i=1;i<=n;i++) fa[i]=i; //初始化,每個點都分別在一個不同的集合裡; for(II i=1;i<=m;i++) { if(find(aa[i].x)!=find(aa[i].y)){ //判斷是否在一個集合裡; join(find(aa[i].x),find(aa[i].y)); //連入樹中; ans+=aa[i].flow; //ans加上當前邊權; } } cout<<ans<<endl; return 0; }

由於時間緊迫,這篇文章沒有圖片解釋,見諒;
by aTm;
END;