题意
有两颗各含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包含编号的总分的最大值。
题解
考虑到选的集合很不确定,不好下手
加上n比较小,所以我们可以枚举一个一定要选的点
那么就变成有根树了
那么遍历两棵树,就可以得到一些依赖关系
通过依赖关系做最大权闭合子图就可以了
我因为比较傻逼,所以缩了点
最难的就是图的个数比较多。。我的变量不够用
送分的八级题啊
CODE:
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int MAX=(1<<30);
const int N=55*55*2;
int n;
int val[N];
struct qq
{
int x,y,last;
}e[N];int num,last[N];
qq E[N];int Last[N];
qq s[N];int last1[N];
void init (int x,int y){num++;e[num].x=x;e[num].y=y;e[num].last=last[x];last[x]=num;}
void Init (int x,int y){num++;E[num].x=x;E[num].y=y;E[num].last=Last[x];Last[x]=num;}
void init1 (int x,int y){num++;s[num].x=x;s[num].y=y;s[num].last=last1[x];last1[x]=num;}
struct qt
{
int x,y,z,last;
}o[N];int onum,olast[N];
void dfs (int x,int fa)
{
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
if (y==fa) continue;
init1(y,x);
dfs(y,x);
}
}
void dfs1 (int x,int fa)
{
for (int u=Last[x];u!=-1;u=E[u].last)
{
int y=E[u].y;
if (y==fa) continue;
// printf("SB:%d %d\n",y,x);
init1(y,x);
dfs1(y,x);
}
}
int dfn[N],low[N],belong[N],sta[N],id,cnt,top;
bool in[N];
int g[N];
void Dfs (int x)
{
// printf("%d\n",x);
low[x]=dfn[x]=++id;
sta[++top]=x;in[x]=true;
for (int u=last1[x];u!=-1;u=s[u].last)
{
int y=s[u].y;
// printf("YES:%d\n",y);
if (dfn[y]==-1)
{
Dfs(y);
low[x]=min(low[x],low[y]);
}
else if (in[y]) low[x]=min(low[x],dfn[y]);
}
if (low[x]==dfn[x])
{
cnt++;g[cnt]=0;int i;
do
{
i=sta[top--];
belong[i]=cnt;
g[cnt]+=val[i];
in[i]=false;
}while (i!=x);
}
}
void oinit (int x,int y,int z)
{
// printf("YES:%d %d %d\n",x,y,z);
onum++;o[onum].x=x;o[onum].y=y;o[onum].z=z;
o[onum].last=olast[x];
olast[x]=onum;
swap(x,y);z=0;
onum++;o[onum].x=x;o[onum].y=y;o[onum].z=z;
o[onum].last=olast[x];
olast[x]=onum;
}
int st,ed;
int h[N];
bool bt ()
{
memset(h,-1,sizeof(h));
queue<int> q;
q.push(st);h[st]=0;
while (!q.empty())
{
int x=q.front();q.pop();
for (int u=olast[x];u!=-1;u=o[u].last)
{
int y=o[u].y;
// printf("%d %d\n",x,y);
if (h[y]==-1&&o[u].z!=0)
{
h[y]=h[x]+1;
q.push(y);
}
}
}
return h[ed]!=-1;
}
int find (int x,int xx)
{
//printf("TKJ");
if (x==ed) return xx;
int s1=0;
for (int u=olast[x];u!=-1;u=o[u].last)
{
int y=o[u].y;
if (h[y]==h[x]+1&&o[u].z>0&&s1<xx)
{
int lalal=find(y,min(xx-s1,o[u].z));
s1+=lalal;
o[u].z-=lalal;
o[u^1].z+=lalal;
}
}
if (s1==0) h[x]=0;
return s1;
}
int solve (int x)//这个点一定要选
{
num=0;memset(last1,-1,sizeof(last1));
dfs(x,0);dfs1(x,0);
memset(dfn,-1,sizeof(dfn));
memset(in,false,sizeof(in));
id=cnt=top=0;
for (int u=1;u<=n;u++)
if (dfn[u]==-1)
Dfs(u);
onum=1;memset(olast,-1,sizeof(olast));
for (int u=1;u<=num;u++)
{
int x=s[u].x,y=s[u].y;
if (belong[x]==belong[y]) continue;
oinit(belong[x],belong[y],MAX);
}
st=cnt+1;ed=st+1;
int sum=0;
for (int u=1;u<=cnt;u++)
{
if (g[u]>0) {sum=sum+g[u];oinit(st,u,g[u]);}
else oinit(u,ed,-g[u]);
}
while (bt()) sum=sum-find(st,MAX);
return sum;
}
int main()
{
num=0;memset(last,-1,sizeof(last));
memset(Last,-1,sizeof(Last));
scanf("%d",&n);
for (int u=1;u<=n;u++)
scanf("%d",&val[u]);
for (int u=1;u<n;u++)
{
int x,y;
scanf("%d%d",&x,&y);
x++;y++;
init(x,y);init(y,x);
}
num=0;
for (int u=1;u<n;u++)
{
int x,y;
scanf("%d%d",&x,&y);
x++;y++;
Init(x,y);Init(y,x);
}
solve(1);
int ans=0;
for (int u=1;u<=n;u++)
ans=max(ans,solve(u));
printf("%d\n",ans);
return 0;
}