最小树形图
定义
在一个有向图中,选定一个点。以这个点为根的生成树(边的方向从父亲指向儿子)叫树形图。所有树形图中边权和最小的叫做最小树形图。
算法
先上一张图:
这个算法是朱-刘算法,复杂度\(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;
}