HDU 2242 考研路茫茫——空调教室【双连通分量+dp】
题意: 有 n 个教室组成一个连通图,知道了m条无向边,现在要去掉一条边使得这些教室分成两个连通的集合,问是否可以做到,如果可以找出两个集合人数
的最小差值。
分析: 因为所有的在同一个双连通分量中的人都必须在一个集合里,可以先求出所有的双连通分量并染色,然后用树形DP求出最小差值即可。
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) #define min(a,b)(a)<(b)?(a):(b) #define maxn 100005 #define maxm 1000005 struct node { int to,next; }e[maxm],ed[maxm]; int tot; int tt; int head[maxn]; int dph[maxn]; void add(int s,int t) { e[tot].to=t; e[tot].next=head[s]; head[s]=tot++; } void add2(int s,int t) { ed[tt].to=t; ed[tt].next=dph[s]; dph[s]=tt++; } int top,sn,ti,n,m,sum; int dfn[maxn]; int low[maxn]; int col[maxn]; int sta[maxn]; int val[maxn]; int w[maxn]; void tarjan(int p,int u) { dfn[u]=low[u]=++ti; sta[++top]=u; int i,k,flag=0; for(i=head[u];i;i=e[i].next) { k=e[i].to; if(k==p&&!flag) { flag=1; continue; } if(dfn[k]==0) tarjan(u,k); low[u]=min(low[u],low[k]); } if(dfn[u]==low[u]) { sn++; do { k=sta[top--]; col[k]=sn; val[sn]+=w[k]; }while(k!=u&&top>0); } } int res; int ab(int x) { return x>0?x:-x; } int dfs(int p,int u) { int i,k; int tmp=val[u]; if(res==0) return 0; for(i=dph[u];i;i=ed[i].next) { k=ed[i].to; if(k!=p) tmp+=dfs(u,k); } res=min(res,ab(sum-2*tmp)); return tmp; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { int i,j,k; sum=0; for(i=0;i<n;i++) { scanf("%d",&w[i]); sum+=w[i]; } int a,b; tot=1; clr(head); while(m--) { scanf("%d%d",&a,&b); add(a,b); add(b,a); } clr(dfn); clr(val); top=sn=ti=0; tarjan(0,0); if(sn==1) { printf("impossible\n"); continue; } tt=1; clr(dph); for(i=0;i<n;i++) for(j=head[i];j;j=e[j].next) { k=e[j].to; if(col[i]!=col[k]) add2(col[i],col[k]); } res=999999999; dfs(0,1); printf("%d\n",res); } return 0; }