题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3197
题解:本题是给定两棵树,每个节点给定初始黑白染色,问在某颗树上最少改变染色次数,使得两数同构(黑白要对应)。
解法:其实如果做过树同构,很容易想到,枚举T2中的节点i为根,与T1的1对应,使两树同构的最小代价。
于是我们可以设状态f[a][b]表示T1中以a为根的子树与T2中以b为根的子树同构的最小代价。
转移的时候使用二分图的最大权匹配,具体来说就是以sonA[],sonB[]作为二分图两侧节点,f[sonA[i]][sonB[j]]的值若合法,则向sonA[i],sonB[j]连一条费用为f[sonA[i]][sonB[j]],容量为1的边。这样做要O(n^3)(如果不考虑费用流复杂度的话,因为每个节点最多10个儿子。)
上面的算法会T,于是想办法优化。
仔细观察可以发现,两棵树的最长链的中点相对应为根必然可以使的树同构,于是只要枚举最长链中点对应起来就好了。(不知道表达的清不清楚)
于是复杂度就降到O(n^2)。。。
/**************************************************************
Problem: 3197
User: hta
Language: C++
Result: Accepted
Time:224 ms
Memory:3340 kb
****************************************************************/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;
const int maxn=703;
int n,f[maxn][maxn],ans,key[maxn],Tkey=0;
struct Ttree
{
int Link[maxn],pre[maxn*2],t[maxn*2],son[maxn],h[maxn],tot,mark[maxn],fa[maxn],N,node[maxn],cnt[maxn],s[maxn];
void init()
{
tot=0;memset(Link,0,sizeof(Link));
}
void add(int x,int y)
{
pre[++tot]=Link[x]; Link[x]=tot; t[tot]=y;
pre[++tot]=Link[y]; Link[y]=tot; t[tot]=x;
}
void bfs(int x)
{
int front=0,rear=1;
memset(h,0,sizeof(h));
s[front]=x,h[x]=1;fa[x]=0;
while(front!=rear)
{
int p=s[front];son[p]=1;cnt[p]=0;
for(int i=Link[p];i;i=pre[i])
if(h[t[i]]==0)h[t[i]]=h[p]+1,fa[t[i]]=p,s[rear++]=t[i],cnt[p]++;
front++;
}
for(int i=rear-1;i>=0;i--)son[fa[s[i]]]+=son[s[i]];
}
void make(int x)
{
N=0;
for(int i=Link[x];i;i=pre[i])
if(h[x]<h[t[i]])node[++N]=t[i];
}
}A,B;
struct Tedge
{
int s,t,v,f,op,pre;
Tedge(){}
Tedge(int _s,int _t,int _f,int _v,int _op,int _pre){s=_s,t=_t,f=_f,v=_v,op=_op,pre=_pre;}
};
const int inf=1000000000;
struct Tgraph
{
int Link[100],tot,source,sink,dist[100],prev[100],vis[100],s[10000];
Tedge g[maxn];
void init(int x,int y)
{
tot=0,memset(Link,0,sizeof(Link));
source=x+y+1,sink=source+1;
}
void add(int x,int y,int f,int v)
{
int tp;
tp=Link[x]; Link[x]=++tot; g[tot]=Tedge(x,y,f,v,tot+1,tp);
tp=Link[y]; Link[y]=++tot; g[tot]=Tedge(y,x,0,-v,tot-1,tp);
}
bool spfa()
{
for(int i=1;i<=sink;i++)dist[i]=inf,vis[i]=0;
int front=0,rear=1;
vis[source]=1,s[front]=source,dist[source]=0;
while(front!=rear)
{
int p=s[front];
for(int i=Link[p];i;i=g[i].pre)
if(g[i].f>0&&dist[g[i].t]>dist[p]+g[i].v)
{
dist[g[i].t]=dist[p]+g[i].v;prev[g[i].t]=i;
if(!vis[g[i].t])vis[g[i].t]=1,s[rear++]=g[i].t;
}
front++;vis[p]=0;
}
return dist[sink]!=inf;
}
int MinCostMaxFlow(int aim)
{
int flow=0,cost=0;
while(spfa())
{
int minf=inf;
for(int i=sink;i!=source;i=g[prev[i]].s)minf=min(minf,g[prev[i]].f);
flow+=minf,cost+=minf*dist[sink];
for(int i=sink;i!=source;i=g[prev[i]].s)g[prev[i]].f-=minf,g[g[prev[i]].op].f+=minf;
}
if(flow!=aim)return -1;else return cost;
}
}G;
inline int get()
{
int f=0,v=0;char ch;
while(!isdigit(ch=getchar()))if(ch=='-')break;
if(ch=='-')f=1;else v=ch-48;
while(isdigit(ch=getchar()))v=v*10+ch-48;
if(f==1)return -v;else return v;
}
int dfs(int a,int b)
{
if(A.son[a]!=B.son[b]||A.cnt[a]!=B.cnt[b])return f[a][b]=-1;
for(int i=A.Link[a];i;i=A.pre[i])
for(int j=B.Link[b];j;j=B.pre[j])
if(A.h[A.t[i]]>A.h[a]&&B.h[B.t[j]]>B.h[b])
f[A.t[i]][B.t[j]]=dfs(A.t[i],B.t[j]);
A.make(a),B.make(b);
int n1=A.N,n2=B.N;
G.init(n1,n2);
for(int i=1;i<=n1;i++)G.add(G.source,i,1,0);
for(int i=1;i<=n2;i++)G.add(i+n1,G.sink,1,0);
for(int i=1;i<=n1;i++)
for(int j=1,cost;j<=n2;j++)
if((cost=f[A.node[i]][B.node[j]])!=-1)G.add(i,j+n1,1,cost);
int res=G.MinCostMaxFlow(n1);
return f[a][b]=res==-1?-1:res+(A.mark[a]!=B.mark[b]);
}
int dist[maxn],fa[maxn];bool vis[maxn];
void dfs2(int x,int pa)
{
dist[x]=dist[pa]+1;vis[x]=1;fa[x]=pa;
for(int i=A.Link[x];i;i=A.pre[i])
if(!vis[A.t[i]])dfs2(A.t[i],x);
}
void findroot()
{
memset(dist,0,sizeof(dist));
memset(vis,0,sizeof(vis));
dist[0]=-1;dfs2(1,0);
int p=1,q=1;
for(int i=2;i<=n;i++)if(dist[p]<dist[i])p=i;
memset(dist,0,sizeof(dist));
memset(vis,0,sizeof(vis));
dist[0]=-1,dfs2(p,0);
for(int i=2;i<=n;i++)if(dist[q]<dist[i])q=i;
if(dist[q]%2==0)
{
int tp=dist[q]/2;
for(int i=1;i<=tp;i++)q=fa[q];
key[Tkey=1]=q;
}else
{
int tp=dist[q]/2;
for(int i=1;i<=tp;i++)q=fa[q];
key[Tkey=1]=q;key[++Tkey]=fa[q];
}
}
int main()
{
n=get();
for(int i=1;i<n;i++)
{
int x=get(),y=get();
A.add(x,y),B.add(x,y);
}
for(int i=1;i<=n;i++)A.mark[i]=get();
for(int i=1;i<=n;i++)B.mark[i]=get();
ans=n*n;
findroot();
for(int i,j,y=1;y<=Tkey;y++)
{
memset(f,-1,sizeof(f));
i=key[1];j=key[y];
A.bfs(i),B.bfs(j);
if(dfs(i,j)!=-1)ans=min(ans,f[i][j]);
}
printf("%d\n",ans);
return 0;
}