[Sdoi2013]assassin

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


你可能感兴趣的:([Sdoi2013]assassin)