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

来源:互联网 发布:犬神带什么御魂 知乎 编辑:程序博客网 时间:2024/04/29 09:04

考研路茫茫——空调教室


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


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