hdu 2242
http://acm.hdu.edu.cn/showproblem.php?pid=2242
题目大意:给一个教室群,问能不能把这些教室群分成两部分,并且使两部分之间的权值差最小(每一个教室都有一定的权值);
思路:先用双联通进行缩点,因为那些双向连通的教室肯定是分不开的,所以要把它们缩成一个点。
之后再重新建图,遍历一次树!
需要注意一点这道题的数据包含重边,需要考虑!!

#include<stdio.h> #include<string.h> #include<stdlib.h> const int N = 10005; struct nd { int from,to,next; }edge[N*5]; int head[N],n,m,dfn[N],low[N],vis[N],val[N],val1[N],tsum,ans,as[N],idx,st[N],cnt; int min(int x,int y){ return x>y?y:x;} void add(int a,int b,int tot) { edge[tot].from = a; edge[tot].to = b; edge[tot].next = head[a]; head[a] = tot; } void tarjan(int pre,int u,int& count) { int j,v,flag = 0; dfn[u] = low[u] = idx++; vis[u] = 1; st[cnt++] = u; for(j = head[u]; j != -1; j=edge[j].next) { v = edge[j].to; if(v==pre&&!flag){flag=1;continue;}//重边处理 if(!vis[v]) tarjan(u,v,count); low[u] = min(low[u],low[v]); } if(dfn[u]==low[u]) { count++; do{ v = st[--cnt]; as[v] = count; val[count] += val1[v]; }while(v!=u&&cnt>=0); } } int dfs(int u,int pre) { int j,v,sum = val[u]; if(!ans)return 0; for(j = head[u]; j != -1; j = edge[j].next) { v = edge[j].to; if(v!=pre) sum += dfs(v,u); } ans = min(ans,abs(tsum-sum*2)); return sum; } int main() { int i,a,b,tot,count; while(scanf("%d %d",&n,&m)==2) { memset(head,-1,sizeof(head)); tsum = 0; tot = 0; for(i = 0; i < n; ++ i){ scanf("%d",val1+i); tsum += val1[i]; } for(i = 0; i < m; ++ i) { scanf("%d %d",&a,&b); add(a,b,tot); tot++; add(b,a,tot); tot++; } memset(vis,0,sizeof(vis)); memset(val,0,sizeof(val)); count = 0; idx = 0; tarjan(0,0,count); if(count==1){ puts("impossible"); continue;} memset(head,-1,sizeof(head)); int k = 0; for(i = 0; i < tot; ++ i) { a = edge[i].from; b = edge[i].to; if(as[a]!=as[b]){ add(as[a],as[b],k);++k;} } cnt = 0; ans = 1<<30; dfs(1,0); printf("%d\n",ans); }return 0; }