1. 程式人生 > >有向圖最小生成樹——最小樹形圖(朱…

有向圖最小生成樹——最小樹形圖(朱…

對於有向圖的最小生成樹 , 也叫做最小樹形圖 。 最小樹形圖的第一個演算法是1965年朱永津和劉振巨集提出的複雜度為O(VE)的演算法。 值得我們驕傲啊 。
下面來分享這個演算法 。
1、求最小樹形圖之前一定要確定根 , 確定根之後再去驗證是否存在樹形圖(這個很簡單 , 就是從根節點能不能到其他點) 。

2、如果存在樹形圖 , 然後再消掉自環 , 因為對於最小樹形圖 , 是肯定不能存在自環的(還有原因後面會講到) 。

3、前面是兩個預處理 。 求每個節點入邊的最小值(除根節點) , 然後再把(求每個節點入邊的最小值的邊)這些邊單獨拿出來 , 看它們之間是否存在環(這就是為什麼要先消除自環), 如果不存在那麼這就是最小樹形圖。

4、如果存在環 , 那麼就開始消環 , 用一個結點來取代這個環中的所有點 , 然後再把其放到原圖中 , 構成新的圖 , 並且圖中的邊的權值要改變為:grap[i].c(原邊的權值) -= min_cost[v](該邊出點的最小入邊值); , 然後再回到步驟3 , 重複操作 , 就能得到最小樹形圖。

下面是最小樹形圖的構造圖示:




有向圖最小生成樹鈥斺斪鈽∈饜甕跡ㄖ熗跛惴ǎ




















程式碼:
鄰接表法:

#include
#include
#include
#include
using namespace std;

const int MAXN = 1000;
struct node
{
    int from , to , dist;
    node(int from , int to , int dist)  //  c++中的類應用
    {
        this->from = from ;
        this->to = to;
        this->dist = dist;
    }
};

vectoredge;
int pre[MAXN] , min_dist[MAXN] ;  //分別記錄每個節點入邊的最小值和入邊另外一個點
int n , m , root , ret;

void init()
{
    edge.clear();
    root = 1;
}

bool solve()
{
    ret = 0;  //  記錄最小總權值
    while(1)
    {
        int i;
        for(i = 1; i <= n; i++)  min_dist[i] = 1000000;
        for(i = 0 ; i < edge.size(); i++)  //  求每個節點的最小入邊 ,
        {
            int u = edge[i].from , v = edge[i].to;
            if(edge[i].dist < min_dist[v] && v != u)  //  把自環除開
            {
                min_dist[v] = edge[i].dist;
                //cout<<min_dist[v]<<endl;
                pre[v] = u;
            }
        }
        for(i = 1; i <= n; i++)
            if(min_dist[i] == 1000000 && i != root)  return false;  //  判斷是否存在樹形圖
        int cntnode = 1 , id[MAXN] , vis[MAXN];
        memset(vis , 0 , sizeof(vis));
        memset(id , 0 , sizeof(id));
        min_dist[root] = 0;
        for(i = 1; i <= n; i++)
        {
            ret += min_dist[i] ;
            int v = i;
            while(vis[v] != i && id[v] == -1 && v != root)  // 在這裡記錄自環 , 如果存在自環 , 那最終會回到一開始那個點 ,也就是vis[v]==i
            {