【51nod1325】两棵树的问题
题目
题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=1325
有两颗各含 \(n\) 个点的无根树,每棵树中点分别被编号为 \(0,1,2,....,n-1\);注意两棵树并不保证同构。
另外给一个 \(n\) 长的整数数组 \(\mathrm{Score}[]\),记录 \(n\) 个编号的得分,\(\mathrm{Score}\) 中的每个元素可正可负。
问题的任务是寻找 集合 \(\{0,1,2,3,4,\cdots,n-1\}\) 的一个最优子集 \(\mathrm{subset}\),要求满足以下条件:
- 在第一棵树中,\(\mathrm{subset}\) 中包含的编号对应的点能构成一个连通的子图;即去掉这棵树中所有 \(\mathrm{subset}\) 中不包含的点后,剩下的点依然是一棵连通的树。
- 在第二棵树中,\(\mathrm{subset}\) 中包含的编号对应的点也能构成一个连通的子图;
- 使 \(\mathrm{subset}\) 包含编号的总得分尽可能的大;即 \(\sum_{i\in\mathrm{subset}} \mathrm{Score}[i]\) 能取到尽可能大的值。
输出这个 \(\mathrm{subset}\) 包含编号的总分的最大值。
\(n\leq 50\)。
思路
由于无根树不好进行状态的转移以及计算,所以可以先枚举一个根 \(rt\)。并强制选择这个点。
然后在两棵树中的点都必须直接或间接与 \(rt\) 相连,那么这就引出了若干限制:当两棵树以 \(rt\) 为根时,如果要选择一个点,必须先选择其父节点。
数据范文很小,不难想到用网络流解决问题。其实这已经是一个最大权闭合子图的板子了。每一个被限制的点向限制它的点连边,源点向价值非负的点连流量为价值的边,价值为负数的点向汇点连流量为其价值的绝对值的边。
时间复杂度 \(O(n^4)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=55,M=3000,Inf=1e9;
int n,S,T,ans,sum,maxf,a[N],U[N],V[N],dep[N],cur[N];
struct edge
{
int next,to,flow;
};
struct Netflow
{
int tot,head[N];
edge e[M];
void add(int from,int to,int flow)
{
e[++tot]=(edge){head[from],to,flow};
head[from]=tot;
}
bool bfs()
{
memset(dep,0x3f3f3f3f,sizeof(dep));
memcpy(cur,head,sizeof(head));
queue<int> q;
q.push(S); dep[S]=19260817;
while (q.size())
{
int u=q.front(); q.pop();
for (int i=head[u];~i;i=e[i].next)
{
int v=e[i].to;
if (e[i].flow && dep[v]>dep[u]+1)
{
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]<Inf;
}
int dfs(int x,int flow)
{
if (x==T) return flow;
int used=0,res;
for (int i=cur[x];~i;i=e[i].next)
{
int v=e[i].to; cur[x]=i;
if (e[i].flow && dep[v]==dep[x]+1)
{
res=dfs(v,min(e[i].flow,flow-used));
used+=res;
e[i].flow-=res; e[i^1].flow+=res;
if (used==flow) return flow;
}
}
return used;
}
void dinic()
{
while (bfs())
maxf+=dfs(S,Inf);
}
}netf;
struct Graph
{
int tot,head[N];
edge e[M];
void add(int from,int to,int flow)
{
e[++tot]=(edge){head[from],to,flow};
head[from]=tot;
}
void dfs(int x,int fa)
{
if (fa) netf.add(x,fa,Inf),netf.add(fa,x,0);
for (int i=head[x];~i;i=e[i].next)
if (e[i].to!=fa) dfs(e[i].to,x);
}
}G1,G2;
void prework()
{
memset(netf.head,-1,sizeof(netf.head));
netf.tot=1; maxf=0;
}
int main()
{
memset(G1.head,-1,sizeof(G1.head));
memset(G2.head,-1,sizeof(G2.head));
S=N-1; T=N-2;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if (a[i]>=0) sum+=a[i];
}
for (int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
G1.add(x+1,y+1,0); G1.add(y+1,x+1,0);
}
for (int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
G2.add(x+1,y+1,0); G2.add(y+1,x+1,0);
}
ans=-Inf;
for (int j=1;j<=n;j++)
{
prework();
G1.dfs(j,0); G2.dfs(j,0);
for (int i=1;i<=n;i++)
if (a[i]>=0) netf.add(S,i,a[i]),netf.add(i,S,0);
else netf.add(i,T,-a[i]),netf.add(T,i,0);
netf.dinic();
ans=max(ans,sum-maxf);
}
printf("%d",ans);
return 0;
}