1. 程式人生 > >ALDS1_12_A:Minimun Spanning Tree

ALDS1_12_A:Minimun Spanning Tree

ALDS1_12_A:Minimun Spanning Tree

題目:
https://cn.vjudge.net/problem/Aizu-ALDS1_12_A

程式碼如下:

#include <bits/stdc++.h>
using namespace std;
#define MAX 105
#define NIL (1 << 21)
#define WHITE 0
#define BLACK 1

int n;
int a[MAX][MAX];

//prime演算法的實現
int prime()
{
    int color[MAX],d[MAX],p[MAX],mina,u,sum;
    for(int i = 0;i < n;i++)
    {
        color[i] = WHITE;
        d[i] = NIL;
    }
    d[0] = 0;//初始的時候,與第一個結點連線的權值為0
    while(1)
    {
        mina = NIL;
        u = -1;
        for(int i = 0;i < n;i++)
        {
            if(mina > d[i] && color[i] != BLACK)    //超出權值最小的那條邊,並且將已納入的點除外
            {
                u = i;
                mina = d[i];
            }
        }
        if(u == -1) break;//當所有點都為黑的時候跳出
        color[u] = BLACK;//將連線後的點變黑

        for(int v = 0;v < n;v++)
        {
            if(a[u][v] != -1 && color[v] != BLACK) //排除形成環
            {
                if(d[v] > a[u][v])
                {
                    d[v] = a[u][v];
                    p[v] = u;//標記連線點的起始位置
                }
            }
        }
    }
    sum = 0;
    for(int i = 1;i < n;i++) sum += a[p[i]][i];
    return sum;
}

int main()
{
    scanf("%d",&n);
    for(int i = 0;i < n;i++)
    {
        for(int j = 0;j < n;j++) scanf("%d",&a[i][j]);
    }

    cout << prime() << endl;
    return 0;
}

這道題是要求最小生成樹,運用的是prime演算法,先來簡單介紹一下prime演算法。
prime演算法簡述:
prime演算法在求最小生成數的時候和圖中的邊數無關,只和圖中的頂點數相關,所以prime演算法適合求稠密圖的最小生成樹,演算法的複雜度為o(n * n)。
prime演算法大概步驟:

  1. 首先將圖中所有的定點都放入集合s,再定義一個集合t,此時集合t的元素為0個,也就是一個空集合。
  2. 然後將s集合中的某一個定點拿出並放入t集合中並在s集合中將其刪除,隨後記錄這個點在s集合中與其他連線點的權值,並且選出最短那個權值儲存下來,然後把那個連線點也放入t集合並在s集合中將其刪除,再儲存與這個點相連的權值,並且結合上一步的權值,找出最小權值所連線的那個點…隨後一直重複這個步驟,直到集合s集合為空。
  3. 集合t就是由Prime演算法得到的最小生成樹的結點,依照步驟2的結點連線這些頂點,得到的就是這個圖的最小生成樹。
    需要注意的是每次把一個點放入集合t之後,那麼這個點在之後的連線中就不能再次出現了,否則就可能形成環,而最小生成樹是沒有環的。
    演算法實現的具體過程:
    1.首先定義一個colour陣列,先將圖中所有的結點都初始為“WHITE”(white代表的就是此結點沒有被放入集合t中),並且定義一個d[]陣列,d[]陣列用於記錄連線集合t內頂點與集合s內頂點的邊中權值最小的邊的權值。最後定義一個p[]陣列,這個陣列用於記錄集合t中頂點的父結點。
    2.開始時先把u初始化為-1,d[0] = 0。通過遍歷找出最小權值的邊,並且連線這條邊的兩個頂點都沒有放入到t集合中,用變數u記錄這條邊的末位置。
    3.隨後從u這個末位置出發尋找與其相連線的其他邊的權值,此時應該邊遍歷邊更新d[i]的值(永遠選擇到達那個頂點權值最小的那個),並且記錄p[i]。
    4.重複上述2,3步驟,如果2中遍歷一遍之後u的值依然為-1,代表所有的點都已經放入集合t中了,則跳出迴圈。
    5.最後累加每個點線連線線的權值(此時p[i]就可起到作用)。