zoukankan      html  css  js  c++  java
  • 最小树形图

    题目链接

    双倍经验

    定义

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

    算法

    先上一张图:

    Pic

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

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

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

    • 求最短弧集;
    • 如果有除了根之外孤立的点(即这个点选不出属于它的最短弧,即没有入度),则没有最小树形图。如果没有环,那么就跳到第四步;
    • 将圈收缩,重新构建图,跳到第一步。在缩圈的时候,如果边(u ightarrow 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
    " );
    			break;
    		}
    		Cnt = 0;
    		if( IsTree() ) {
    			printf( "%d
    ", Ans + Cnt );
    			break;
    		}
    		DoIt();
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    Linux 压缩,解压
    Angular 修改子组件的样式
    vue项目-各类卡顿问题记录
    老vue项目webpack3升级到webpack5全过程记录(二)
    老vue项目webpack3升级到webpack5全过程记录(一)
    eslint配置汇总
    cke点击时初始化编辑器后光标恢复的方法
    使用 SVG transform rotate 解决画框中的数字跟随旋转的问题
    关于 Selection(getSelection ) 对象的一些常规用法集锦
    markdown 的使用技巧
  • 原文地址:https://www.cnblogs.com/chy-2003/p/11615784.html
Copyright © 2011-2022 走看看