最小树形图

题目链接

双倍经验

定义

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

算法

先上一张图:

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;
}

posted @ 2019-10-01 18:34  chy_2003  阅读(191)  评论(0编辑  收藏  举报