其他分享
首页 > 其他分享> > 最小树形图

最小树形图

作者:互联网

题目链接

双倍经验

定义

在一个有向图中,选定一个点。以这个点为根的生成树(边的方向从父亲指向儿子)叫树形图。所有树形图中边权和最小的叫做最小树形图。

算法

先上一张图:

Pic

这个算法是朱-刘算法,复杂度\(O(VE)\)。

其中最短弧集指的是:对于每个点(除选定的根之外),选入度中一条边权最小的边组成的图。

上面那一张图其实挺清楚了,下面再阐述一遍:

  • 求最短弧集;
  • 如果有除了根之外孤立的点(即这个点选不出属于它的最短弧,即没有入度),则没有最小树形图。如果没有环,那么就跳到第四步;
  • 将圈收缩,重新构建图,跳到第一步。在缩圈的时候,如果边\(u\rightarrow v\)的\(v\)缩掉了,那么这条边的边权应当减去原先\(v\)的最小弧。同时注意到每个点只有一个入度,所以并不用tarjan(还不是因为不会写),直接瞎搞就好了。。。
  • 最后需要展开求得最小树形图。

参考程序

#include <bits/stdc++.h>
using namespace std;

const int Maxn = 110;
const int Maxm = 10010;
const int INF = 100000000;
struct edge {
    int To, Next, Val;
    edge() {}
    edge( int _To, int _Next, int _Val ) : To( _To ), Next( _Next ), Val( _Val ) {}
};
struct graph {
    int Start[ Maxn ], Used;
    edge Edge[ Maxm ];
    inline void Clear() {
        Used = 0;
        memset( Start, 0, sizeof( Start ) );
        return;
    }
    inline void AddEdge( int x, int y, int z ) {
        for( int t = Start[ x ]; t; t = Edge[ t ].Next ) 
            if( Edge[ t ].To == y ) {
                if( z < Edge[ t ].Val ) Edge[ t ].Val = z;
                return;
            }
        Edge[ ++Used ] = edge( y, Start[ x ], z );
        Start[ x ] = Used;
        return;
    }
};
int n, m, r, Ans, Cnt;
int Min[ Maxn ], From[ Maxn ];
int Cut[ Maxn ], Color[ Maxn ], Time;
int Vis[ Maxn ], Flag;
int Appear[ Maxn ];
graph Prime, MinEdge, Small;

bool Collect() {
    for( int i = 1; i <= n; ++i ) {
        Min[ i ] = INF;
        From[ i ] = 0;
    }
    for( int i = 1; i <= n; ++i ) {
        if( !Appear[ i ] ) continue;
        for( int t = Prime.Start[ i ]; t; t = Prime.Edge[ t ].Next ) {
            int j = Prime.Edge[ t ].To;
            if( j == r ) continue;
            int c = Prime.Edge[ t ].Val;
            if( c < Min[ j ] ) {
                Min[ j ] = c;
                From[ j ] = i;
            }
        }
    }
    for( int i = 1; i <= n; ++i ) {
        if( !Appear[ i ] ) continue;
        if( i == r ) continue;
        if( From[ i ] == 0 ) return false;
    }
    MinEdge.Clear();
    for( int i = 1; i <= n; ++i ) {
        if( !Appear[ i ] ) continue;
        if( i == r ) continue;
        MinEdge.AddEdge( From[ i ], i, Min[ i ] );
    }
    return true;
}

void Dfs( int u, int Fa ) {
    Vis[ u ] = 1;
    for( int t = MinEdge.Start[ u ]; t; t = MinEdge.Edge[ t ].Next ) {
        int v = MinEdge.Edge[ t ].To;
        if( v == Fa ) continue;
        Cnt += MinEdge.Edge[ t ].Val;
        Dfs( v, u );
    }
    return;
}

bool IsTree() {
    memset( Vis, 0, sizeof( Vis ) );
    Dfs( r, 0 );
    for( int i = 1; i <= n; ++i ) {
        if( !Appear[ i ] ) continue;
        if( !Vis[ i ] )
            return false;
    }
    return true;
}

void Dfs2( int u, int Fa ) {
    Color[ u ] = ++Time;
    Vis[ u ] = Flag;
    for( int t = MinEdge.Start[ u ]; t; t = MinEdge.Edge[ t ].Next ) {
        int v = MinEdge.Edge[ t ].To;
        int c = MinEdge.Edge[ t ].Val;
        if( Vis[ v ] && Vis[ v ] != Flag ) continue;
        if( Vis[ v ] ) {
            Color[ u ] = Color[ v ];
            Cut[ v ] = c;
            Ans += c;
            return;
        }
        Dfs2( v, u );
        if( Color[ v ] <= Color[ u ] ) {
            Color[ u ] = Color[ v ];
            Cut[ v ] = c;
            Ans += c;
            return;
        }
    }
    return;
}

void DoIt() {
    memset( Vis, 0, sizeof( Vis ) ); 
    memset( Cut, 0, sizeof( Cut ) );
    memset( Color, 0, sizeof( Color ) );
    Flag = 0;
    Time = 0;
    for( int i = 1; i <= n; ++i ) {
        if( !Appear[ i ] ) continue;
        if( Vis[ i ] ) continue;
        ++Flag;
        Dfs2( i, 0 );
    }
    Small.Clear();
    r = Color[ r ];
    for( int i = 1; i <= n; ++i ) {
        if( !Appear[ i ] ) continue;
        for( int t = Prime.Start[ i ]; t; t = Prime.Edge[ t ].Next ) {
            int j = Prime.Edge[ t ].To;
            int c = Prime.Edge[ t ].Val;
            if( Color[ i ] == Color[ j ] ) continue;
            Small.AddEdge( Color[ i ], Color[ j ], c - Cut[ j ] );
        }
    }
    Prime = Small;
    memset( Vis, 0, sizeof( Vis ) );
    for( int i = 1; i <= n; ++i ) {
        if( !Appear[ i ] ) continue;
        Vis[ Color[ i ] ] = 1;
    }
    for( int i = 1; i <= n; ++i ) Appear[ i ] = Vis[ i ];
    return;
}

int main() {
    scanf( "%d%d%d", &n, &m, &r );
    Ans = 0;
    Prime.Clear();
    for( int i = 1; i <= m; ++i ) {
        int x, y, z;
        scanf( "%d%d%d", &x, &y, &z );
        if( x == y ) continue;
        Prime.AddEdge( x, y, z );
    }
    for( int i = 1; i <= n; ++i ) Appear[ i ] = 1;
    while( true ) {
        if( !Collect() ) {
            printf( "-1\n" );
            break;
        }
        Cnt = 0;
        if( IsTree() ) {
            printf( "%d\n", Ans + Cnt );
            break;
        }
        DoIt();
    }
    return 0;
}

标签:Val,int,最小,树形图,Start,Edge,Maxn
来源: https://www.cnblogs.com/chy-2003/p/11615784.html