1. 程式人生 > >算法筆記:最小生成樹

算法筆記:最小生成樹

pri stream ostream nod struct 子圖 return tdi 規模

摸魚摸了一個算法,打開書,看了一下 感覺其中一個算法就是並查集的縮水版.. (但是我看了半天並沒有看出這個算法用在哪些地方)

描述

給定一張邊帶權的無向圖\(G=(V,E),n = |V|, m = |E|\)\(V\)中全部\(n\)個定點和\(E\)\(n-1\)條邊構成的無向聯通子圖被稱作\(G\)的一棵生成樹。邊的權值之和最小的生成樹被稱為無向圖\(G\)的最小生成樹(MST)

定理

任意一棵最小生成樹一定包含無向圖中權值最小的邊

Kruskal算法

Kruskal在任何時刻,總是從剩余的邊中選出一條權值最小的,而且這條邊的兩個端點屬於生成森林中的兩棵不同的樹(不連通),將該邊加入生成森林。

題目講解

因為這個題目就是板子 所以我就直接發就好了(偷懶 逃..)

題目描述

如題,給出一個無向圖,求出最小生成樹,如果該圖不連通,則輸出orz

輸入格式:

第一行包含兩個整數N、M,表示該圖共有N個結點和M條無向邊。(N<=5000,M<=200000)

接下來M行每行包含三個整數Xi、Yi、Zi,表示有一條長度為Zi的無向邊連接結點Xi、Yi

輸出格式:

輸出包含一個數,即最小生成樹的各邊的長度之和;如果該圖不連通則輸出orz

輸入樣例#1:

4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

輸出樣例#1:

7

說明

時空限制:1000ms,128M

數據規模:

對於20%的數據:N<=5,M<=20

對於40%的數據:N<=50,M<=2500

對於70%的數據:N<=500,M<=10000

對於100%的數據:N<=5000,M<=200000

扔代碼

//#define fre yes

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

const int maxn = 500010;
struct Node{
    int x,y,z;
}edge[maxn];

int par[maxn];
int n,m,ans,tot;

template<typename T>inline void read(T&x)
{
    x = 0;char c;int lenp = 1;
    do { c = getchar();if(c == '-') lenp = -1; } while(!isdigit(c));
    do { x = x * 10 + c - '0';c = getchar(); } while(isdigit(c));
    x *= lenp;
}

bool cmp (Node x,Node y)
{
    return x.z < y.z;
}

int find(int x)
{
    if(par[x] == x) return x;
    else return par[x] = find(par[x]);
}

int main()
{
    read(n);read(m);
    for (int i=1;i<=m;i++)
    {
        int x,y,z;
        read(x);read(y);read(z);
        edge[i].x = x;
        edge[i].y = y;
        edge[i].z = z;
    } sort(edge+1,edge+1+m,cmp);
    for (int i=1;i<=n;i++) par[i] = i;
    for (int i=1;i<=m;i++)
    {
        int x = find(edge[i].x);
        int y = find(edge[i].y);
        if(x == y) continue;

        tot++;
        par[x] = y;
        ans += edge[i].z;
    } if(tot < n-1) puts("orz");
    else printf("%d\n",ans);
    return 0;
}

算法筆記:最小生成樹