题意:给一个无向图连通,图中每个结点都有一个权值,问能否割掉图中的一条边,使得图变为2个连通支,若能,使2个连通支中权值和的差最小,输出差的绝对值。N<=10^4
分析:dfs时用tarjan判断边是否为割边,若是就更新答案。需注意的是有重边。
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 10000 #define M 20000 #define INF 0x3f3f3f3f int n,m,e,w[N]; int sum,num[N]; int first[N],next[M<<1],v[M<<1],cnt[M<<1]; int dfn[N],low[N],id,ans; void init() { e=sum=id=0; ans=INF; memset(first,-1,sizeof(first)); memset(cnt,0,sizeof(cnt)); memset(dfn,0,sizeof(dfn)); memset(num,0,sizeof(num)); } void add(int a,int b) { for(int i=first[a];~i;i=next[i]) { if(v[i]==b) { cnt[i]++; return; } } cnt[e]++; v[e]=b; next[e]=first[a]; first[a]=e++; } void dfs(int a,int fa) { dfn[a]=low[a]=++id; num[a]=w[a]; int i,b; for(i=first[a];~i;i=next[i]) { b=v[i]; if(dfn[b]) { if(b^fa) low[a]=min(low[a],dfn[b]); } else { dfs(b,a); num[a]+=num[b]; low[a]=min(low[a],low[b]); if(low[b]>dfn[a] && cnt[i]<2) { ans=min(ans,abs(sum-2*num[b])); } } } } int main() { int a,b; while(~scanf("%d%d",&n,&m)) { init(); for(int i=0;i<n;i++) { scanf("%d",&w[i]); sum+=w[i]; } for(int i=0;i<m;i++) { scanf("%d%d",&a,&b); add(a,b); add(b,a); } dfs(0,-1); if(ans<INF) printf("%d\n",ans); else puts("impossible"); } return 0; }