题目链接: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; }