51nod 1325 两棵树的问题 最大权闭合子图
来源:互联网 发布:詹姆斯去年总决赛数据 编辑:程序博客网 时间:2024/05/21 09:45
题意
有两颗各含N个点的无根树,每棵树中点分别被编号为0,1,2,….,N-1;注意两棵树并不保证同构。
另外给一个N长的整数数组Score[],记录N个编号的得分,Score中的每个元素可正可负。
问题的任务是寻找 集合{0,1,2,3,4,…,N-1}的一个最优子集subset,要求满足以下条件:
1)在第一棵树中,subset中包含的编号对应的点能构成一个连通的子图;即去掉这棵树中所有subset中不包含的点后,剩下的点依然是一棵连通的树。
2)在第二棵树中,subset中包含的编号对应的点也能构成一个连通的子图;
3)使subset包含编号的总得分尽可能的大;即SUM{ Score[i] | i∈subset }能取到尽可能大的值。
输出这个subset包含编号的总分的最大值。
2<=N<=50,-1000<=Score[i]<=1000
分析
首先我们可以枚举两棵子树的根,然后继续搞事。
注意到如果选了点x,那么在每一棵树上x到根路径上的所有点都要选。我们可以把这看成一个限制关系,如果选了x就一定要选x在两棵树中的父亲。然后直接上最大权闭合子图即可。
代码
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int N=55;const int inf=1000000000;int n,cnt,last[N],ls1[N],ls2[N],cur[N],dis[N],s,t,val[N];struct edge{int to,next,c;}e[N*N*10];queue<int> que;int read(){ int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}void addedge(int u,int v,int c){ e[++cnt].to=v;e[cnt].c=c;e[cnt].next=last[u];last[u]=cnt; e[++cnt].to=u;e[cnt].c=0;e[cnt].next=last[v];last[v]=cnt;}void build(int x,int fa,int op){ if (fa) addedge(x,fa,inf); if (op==1) { for (int i=ls1[x];i;i=e[i].next) if (e[i].to!=fa) build(e[i].to,x,op); } else { for (int i=ls2[x];i;i=e[i].next) if (e[i].to!=fa) build(e[i].to,x,op); }}bool bfs(){ for (int i=s;i<=t;i++) dis[i]=0; while (!que.empty()) que.pop(); dis[s]=1;que.push(s); while (!que.empty()) { int u=que.front();que.pop(); for (int i=last[u];i;i=e[i].next) if (e[i].c&&!dis[e[i].to]) { dis[e[i].to]=dis[u]+1; if (e[i].to==t) return 1; que.push(e[i].to); } } return 0;}int dfs(int x,int maxf){ if (x==t||!maxf) return maxf; int ret=0; for (int &i=cur[x];i;i=e[i].next) if (e[i].c&&dis[e[i].to]==dis[x]+1) { int f=dfs(e[i].to,min(e[i].c,maxf-ret)); e[i].c-=f; e[i^1].c+=f; ret+=f; if (maxf==ret) break; } return ret;}int dinic(){ int ans=0; while (bfs()) { for (int i=s;i<=t;i++) cur[i]=last[i]; ans+=dfs(s,inf); } return ans;}int main(){ n=read(); for (int i=1;i<=n;i++) val[i]=read(); for (int i=1;i<n;i++) { int x=read()+1,y=read()+1; e[++cnt].to=y;e[cnt].next=ls1[x];ls1[x]=cnt; e[++cnt].to=x;e[cnt].next=ls1[y];ls1[y]=cnt; } for (int i=1;i<n;i++) { int x=read()+1,y=read()+1; e[++cnt].to=y;e[cnt].next=ls2[x];ls2[x]=cnt; e[++cnt].to=x;e[cnt].next=ls2[y];ls2[y]=cnt; } s=0;t=n+1;int tmp=cnt; if (!(tmp&1)) tmp++; int ans=0; for (int r1=1;r1<=n;r1++) for (int r2=1;r2<=n;r2++) { int sum=0;cnt=tmp; for (int i=s;i<=t;i++) last[i]=0; for (int i=1;i<=n;i++) if (val[i]>0) sum+=val[i],addedge(s,i,val[i]); else addedge(i,t,-val[i]); build(r1,0,1); build(r2,0,2); sum-=dinic(); ans=max(ans,sum); } printf("%d",ans); return 0;}
阅读全文