HDU_2242 考研路茫茫——空调教室 tarjin+树形dp

http://acm.hdu.edu.cn/showproblem.php?pid=2242

题意:给你一个有N个点,M条边的无向图,并给没个结点一个权值,要求去掉所有边中的一条,将图分成两部分,求此时的两个图的权值差的最小值。

思路:先对原图进行缩点,就可以得到一棵树,再用一个树形dp求出最小的权值差。注意重边的处理。

代码:

#include
#include
#include
#include
const int MAXN = 50010 ;
const int MAXM = 100010 ;
int N ,M ;
int num[MAXN] ;
struct Node{
    int num ,next ;
}edge[MAXM*2] ;

int root[MAXN] ,e_cnt ;

struct Node1{
    int num ,next ;
}ex[MAXM*2];
int r[MAXN] , r_cnt ;
int sum[MAXN] ,tot ,ans;

void init(){
    memset(root, -1, sizeof(root));
    e_cnt = 0 ;
    r_cnt = 0 ;
    memset(r, -1, sizeof(r));
}
void add(int a, int b ){
    edge[e_cnt].num = b ;
    edge[e_cnt].next = root[a];
    root[a] = e_cnt++ ;
}
int dfn[MAXN] , low[MAXN] , stack[MAXN] ,id[MAXN];
int top , idx , cnt ;

void tarjin(int x, int pre){
    low[x] = dfn[x] = ++idx ;
    stack[top++] = x ;
    bool f = 1 ;
    for(int i=root[x];i!=-1;i=edge[i].next){
        int u = edge[i].num ;
        if(u==pre && f){            //避免重边,第一次的时候直接跳过
            f = 0 ; continue ;
        }
        if( dfn[u] == -1){
            tarjin(u,x);
            if( low[u] < low[x])    low[x] = low[u] ;
            else if( low[u] > dfn[x] ){
                for( stack[top]=-1 ;stack[top]!=u ;){
                    top-- ;
                    int v = stack[top] ;
                    id[ v ] = cnt ;
                }
                cnt ++ ;
            }
        }
        else if( dfn[u] < low[x])
            low[x] = dfn[u] ;
    }
}

void add1(int a ,int b){
    ex[r_cnt].num = b ;
    ex[r_cnt].next = r[a] ;
    r[a] = r_cnt++ ;
}
int dfs(int u,int pre){
    int res = sum[u] ;
    for(int i=r[u];i!=-1;i=ex[i].next){
        int v = ex[i].num ;
        if(v == pre)    continue ;
        int temp = dfs(v,u) ;
        res += temp ;
    }
    if(ans > abs( tot-res*2) )
        ans = abs( tot - res*2 ) ;
    return res ;
}
void solve(){
    top = idx = cnt = 0 ;
    memset(dfn , -1 ,sizeof(dfn));
    memset(id , 0 ,sizeof(id));
    memset(sum , 0, sizeof(sum));
    cnt = 1 ;
    for(int i=1;i<=N;i++){
        if( dfn[i] == -1 ){
            tarjin(i, -1) ;
        }
    }
    if(cnt == 1){
        printf("impossible\n") ;
        return ;
    }
    for(int i=1;i<=N;i++){
        sum[ id[i] ]  += num[i] ;
    }
    for(int i=1;i<=N;i++){
        for(int j=root[i] ;j!=-1;j=edge[j].next){
            int v = edge[j].num ;
            if( id[i] != id[v] ){
                add1( id[i],id[v] ) ;
            }
        }
    }
    ans = (1<<30) ;
    dfs(1,-1);
    printf("%d\n",ans) ;
}
int main(){
    int a ,b ;
    while(scanf("%d%d",&N,&M) == 2){
        tot = 0;
        for(int i=1;i<=N;i++){
            scanf("%d",&num[i]);
            tot += num[i];
        }
        init() ;
        for(int i=1;i<=M;i++){
            scanf("%d%d",&a,&b);
            a++ ;b ++ ;
            add(a,b); add(b,a);
        }
        solve() ;
    }
    return 0 ;
}


你可能感兴趣的:(ACM-图论)