http://acm.hdu.edu.cn/showproblem.php?pid=2242
题意:给你一个有N个点,M条边的无向图,并给没个结点一个权值,要求去掉所有边中的一条,将图分成两部分,求此时的两个图的权值差的最小值。
思路:先对原图进行缩点,就可以得到一棵树,再用一个树形dp求出最小的权值差。注意重边的处理。
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
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 ;
}