HDU 2242 考研路茫茫—空调教室 (边双连通+树形DP)

<题目链接>

题目大意:

给定一个连通图,每个点有点权,现在需要删除一条边,使得整张图分成两个连通块,问你删除这条边后,两联通块点权值和差值最小是多少。

解题分析:

删除一条边,使原连通图分成两个连通分量,所以删除的那条边必然是桥。为了得到所有的桥,我们对原图进行边双连通图缩点。然后对缩点后的新图,跑一遍树形DP,得到所有桥两端点权和的最小差值。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define clr(a,b) memset(a,b,sizeof(a))
 5 const int N = 1e4+5, M = 2e4+5;
 6 struct Edge{
 7     int from,to,nxt;
 8 }edge[M<<1],edge1[M<<1];
 9 
10 int n,m,cnt,cnt1,sum,ans,dcc,tot,top;
11 int head[N],head1[N],instk[N],bel[N],dfn[N],low[N],val[N],val1[N],cost[N],stk[N];
12 
13 void init(){
14     cnt=tot=sum=dcc=cnt1=0;ans=1e9;
15     clr(dfn,0);clr(low,0);clr(val,0);clr(head,-1);clr(head1,-1);
16     clr(instk,0);clr(cost,0);
17 }
18 void addedge(int u,int v){ edge[cnt].from=u;edge[cnt].to=v;edge[cnt].nxt=head[u];head[u]=cnt++; }
19 void addedge1(int u,int v){ edge1[cnt1].from=u;edge1[cnt1].to=v;edge1[cnt1].nxt=head1[u];head1[u]=cnt1++; }
20 
21 void Tarjan(int u,int fa){    //Tarjan找边双连通分量并进行缩点
22     dfn[u]=low[u]=++tot;
23     instk[u]=1;stk[++top]=u;
24     int flag=0;
25        for(int i=head[u];~i;i=edge[i].nxt){
26            int v=edge[i].to;
27            if(v==fa && !flag){ flag=1;continue; }    //跳过搜索树上的边,这种写法能够处理重边的情况
28            if(!dfn[v]){
29                Tarjan(v,u);
30                low[u]=min(low[u],low[v]);
31            }else if(instk[v])low[u]=min(low[u],dfn[v]);
32        }
33     if(dfn[u]==low[u]){
34         ++dcc;
35         while(true){
36             int v=stk[top--];
37             bel[v]=dcc;
38             val[dcc]+=val1[v];
39             if(v==u)break;
40         }
41     }
42 }
43 void dfs(int u,int pre){    //树形DP得到桥两边差值的最小值
44     cost[u]=val[u];
45     for(int i=head1[u];~i;i=edge1[i].nxt){
46         int v=edge1[i].to;
47         if(v==pre)continue;
48         dfs(v,u);
49         cost[u]+=cost[v];
50     }
51     ans=min(ans,abs(sum-2*cost[u]));
52 }
53 int main(){
54     while(scanf("%d%d",&n,&m)!=EOF){
55         init();
56         for(int i=0;i<n;i++)
57             scanf("%d",&val1[i]),sum+=val1[i];
58         for(int i=1;i<=m;i++){
59             int u,v;scanf("%d%d",&u,&v);
60             addedge(u,v),addedge(v,u);
61         }
62         Tarjan(0,0);
63         if( dcc==1 ) { puts("impossible");continue; }   //如果该图是边双连通图,说明没有桥
64         for(int i=0;i<cnt;i++){
65             int u,v;u=edge[i].from;v=edge[i].to;
66             if(bel[u]!=bel[v])addedge1(bel[u],bel[v]);     //建立单向边
67         }
68         dfs(1,-1);
69         printf("%d\n",ans);
70     }
71 }

 

 

2019-03-02

posted @ 2019-03-01 23:12  悠悠呦~  阅读(233)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end