hdu 2242 考研路茫茫——空调教室

考研路茫茫——空调教室


非常好的一个题。这个题目与hdu 3298很相似,都是求分成两个连通块之后,差值最小。不过这个题目的难点在于给的不是一棵树,而是一个图有回路存在。由于只能切断一条边,所以这条边对于这个图来说,一定是割边。那么问题就变得容易的多了,枚举每条割边,算出割边去除后两个连通分量的权值和之差。如何快速计算出每个连通块的权值和。我们可以在找割边的时候,就算出每个点的子节点的权值和,注意对双连通块的处理。这里如果搜到祖先节点,那么权值就需要相加,否则累加,然后传给父亲借点。有很多人是先求出割边,然后缩点,这样就构成一颗树不过那样太麻烦了。。。


/*
    *author    : cuschenan
    *prog      : hdu2242
    *algorithm : 无向图求割边+TREE DP 对图进行DFS,
    *            在DFS的时候找割边,同时计算出每个点的子节点的个数
    *            然后对于割边计算出差值。
    *            这个题首先要是的不连通,我们可以很容易的想到找割边,但是要求
    *            去掉某条边后,差值最小,我们就可以考虑将在同一个双连通分量中的
    *            点进行缩点,然后重构成树,保留下来的边,很明显就是图的割边,我们
    *            没必要去重新构图,只需要在找到割边的时候进行计算,重新构图后还是对
    *            割边进行计算。只不过需要将孩子节点的个数传递过来。注意数组稍微开大一点
    *2012-09-14 00:29:58	Accepted	2242	93MS	1028K	1852 B	C++	csu_chenan
*/
#include 
#include 
#define maxn 10005
int num[maxn] ;
int first[maxn] ;
int next[maxn<<2] ;
int u[maxn<<2] ;
int dfn[maxn] ;
int low[maxn] ;
int col[maxn] ;

int depth ;
int n , m ;
int sum   ;
int ans   ;
void add_edge(int p , int q , int e){
	next[e] = first[p] ;
	first[p] = e ;
	u[e] = q ;
}
inline void init(){
	memset(dfn , 0 , sizeof(int)*n) ;
	memset(low , 0 , sizeof(int)*n) ;
	memset(next , -1 , sizeof(int) * 2 * m) ;
	memset(col  , 0  , sizeof(int) * n ) ;
	memset(first, -1 , sizeof(int) * n);
}
bool read(){
	if(scanf("%d%d" , &n , &m) == EOF)
		return 0 ;
	sum = 0 ;
	for(int i = 0 ; i < n ; i ++){
		scanf("%d" , &num[i]) ;
		sum += num[i] ;
	}
	int p , q ;
	init() ;
	int e = 1 ;
	for(int i = 1 ; i <= m ; i ++){
		scanf("%d%d" , &p , &q) ;
		add_edge(p , q , e ++) ;
		add_edge(q , p , e ++) ;
	}
	return 1 ;
}
inline int min(int x , int y){
	return x < y ? x : y ;
}
int dfs(int v , int f){
	int cnt , tmp ;
	depth ++ ;
	low[v] = depth ;
	dfn[v] = depth ;
	col[v] = 1 ;
    cnt = num[v] ;
    int flag = 0 ;
    int dpv  = 0x3f3f3f3f ;
	for(int i = first[v] ; i != -1 ; i = next[i]){
		int x = u[i] ;

		if(x == f && !flag){
            flag = 1 ;
			continue ;
		}

		if(col[x] == 1){
			low[v] = min(low[v] , dfn[x]) ;
		}
		if(col[x] == 0){
			tmp = dfs(x , v) ;
			cnt += tmp ;
			low[v] = min(low[v] , low[x]) ;
			if(low[x] > dfn[v]){
				int c = sum - tmp * 2 ;
				c = c > 0 ? c : -c ;
				dpv = min(dpv , c) ;
			}
		}
	}
	ans = min(ans , dpv) ;
	col[v] = 2 ;
	return cnt ;
}
void solve(){
	depth = 0 ;
	ans = 0x3f3f3f3f ;
	dfs(0 , -1) ;
	if(ans < 0x3f3f3f3f)
        printf("%d\n" , ans) ;
    else{
        puts("impossible") ;
    }
}
int main(){
	while(read()){
		solve() ;
	}
	return 0 ;
}



你可能感兴趣的:(树形DP,hduoj,割边,algorithm,tree,c)