题意:给你N个点,M条无向边,并且每个点有一个权值,问把哪条边去掉,能使图分成两个部分,并且这两个部分的权值差最小。
跑一次Tarjan转化成一棵树,然后跑一次树型DP(特别注意重边的处理)。
#include <iostream> #include <cstdio> #include <cstring> #include <stack> using namespace std; const int N=10005; struct Edge { int v; Edge* nxt; }memo[N*10],*cur,*org[N],*adj[N]; int n,m,mi,sum,valu[N],cnt[N]; int dfn[N],low[N],id[N],idx,scnt; bool insta[N],vis[N]; stack<int> st; int iabs(int x){return x>0?x:-x;} void dfs(int u,int fa) { vis[u]=1; for(Edge* it=adj[u];it;it=it->nxt) { int v=it->v; if(v==fa||vis[v]) continue; dfs(v,u); cnt[u]+=cnt[v]; } mi=min(mi,iabs(sum-2*cnt[u])); } void tarjan(int u,int fa) { dfn[u]=low[u]=idx++; st.push(u); insta[u]=1; int v,flag=1; for(Edge* it=org[u];it;it=it->nxt) { v=it->v; if(v==fa&&flag){flag=0;continue;} if(!dfn[v]) { tarjan(v,u); low[u]=min(low[u],low[v]); } else if(insta[v]&&dfn[v]<low[u]) low[u]=dfn[v]; } if(low[u]==dfn[u]) { while(1) { int top=st.top(); st.pop(); insta[top]=0; id[top]=scnt; if(top==u) break; } scnt++; } } void addEdge(int u,int v,Edge* head[]) { cur->v=v; cur->nxt=head[u]; head[u]=cur++; } void init() { cur=memo; idx=1,scnt=1,sum=0,mi=(1<<30); memset(adj,0,sizeof(adj)); memset(org,0,sizeof(org)); memset(dfn,0,sizeof(dfn)); memset(insta,0,sizeof(insta)); memset(cnt,0,sizeof(cnt)); memset(vis,0,sizeof(vis)); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); int u,v; for(int i=0;i<n;i++) scanf("%d",&valu[i]); for(int i=0;i<m;i++) { scanf("%d%d",&u,&v); addEdge(u,v,org); addEdge(v,u,org); } for(int i=0;i<n;i++) if(!dfn[i]) tarjan(i,-1); if(scnt<=2) puts("impossible"); else { for(int i=0;i<n;i++) { cnt[id[i]]+=valu[i]; sum+=valu[i]; for(Edge* it=org[i];it;it=it->nxt) { u=id[i],v=id[it->v]; if(u!=v) addEdge(u,v,adj),addEdge(v,u,adj); } } dfs(1,-1); printf("%d\n",mi); } } return 0; }