2020牛客多校10-J- Identical Trees-树形dp+建图费用流

题目链接

题面:

在这里插入图片描述
在这里插入图片描述

大致题意:

给两颗树,然后有一种操作就是修改树的结点编号,现在让两棵树变成相同的,问最少需要修改几次?

分析一波:

先声明下:
  1. x1 是第一棵树,他的一些子节点 (子树) y11,y12,y13…
  2. x2 是第二棵树,他的一些子节点 (子树) y21,y22,y23…

1: 两个树必须要同构,才能修改成一样的。

为啥要匹配?
修改的时候,假如在 x1 的子树 和 x2 的子树中,同构的子树,有好几类,然后 每类中,y11 可能变成 y22 要优与变成 y21…总之汇合起来,我们就要得到一个两两成匹配的方案,所以就变成了一个典型的二分图最大权值匹配问题 .

so:权值就是 两个子树的节点变成一致的修改次数。

整棵树的 最少操作数, 就dfs后序,对每颗同深度的子树 进行匹配,最后到达两棵树的根节点

#include<bits/stdc++.h>
#define ks ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define fr first
#define sc second
#define pb push_back
#define pf push_front
#define mp make_pair
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pa;
typedef set<int>::iterator sit;
typedef multiset<int>::iterator msit;
template<class T>inline void read(T &res){
    char c;T flag=1;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
    while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
void wenjian(){freopen("concatenation.in","r",stdin);freopen("concatenation.out","w",stdout);}
void tempwj(){freopen("hash.in","r",stdin);freopen("hash.out","w",stdout);}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;}
struct chongzai{int c; bool operator<(const chongzai &b )const{ return c>b.c; } }sss;

const int maxn=1e3+177;
const int maxm=1e6+177;
const ll mod=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;

ll dp[maxn][maxn];

vector<int >G[3][maxn];
string zxbs[3][maxn];
int dia[maxn];
int n;
int indexx;

struct Node{
	int next;
	int to;
	ll vul;
	ll cost;
}edge[maxm];

int head[maxm];
int tot;

void add(int from,int to,ll vul,ll cost){
	edge[++tot].next=head[from];
	edge[tot].to=to;
	edge[tot].vul=vul;
	edge[tot].cost=cost;
	head[from]=tot;


	edge[++tot].next=head[to];
	edge[tot].to=from;
	edge[tot].vul=0;
	edge[tot].cost=-cost;
	head[to]=tot;


}


void getzxbs(int x,int f){

	vector<string>vt;
	zxbs[f][x]+='(';
	for(int i=0;i<G[f][x].size();i++){
		int v=G[f][x][i];
		getzxbs(v,f);
		vt.pb(zxbs[f][v]);
	}
	sort(vt.begin(),vt.end());
	for(int i=0;i<vt.size();i++){
		zxbs[f][x]+=vt[i];
	}
	zxbs[f][x]+=')';
}


void init(int nn){
    for(int i=0;i<nn+17;i++){
        head[i]=0;
    }
    tot=1;
}
int s,t;  // 汇点  源点
ll dis[maxn],incf[maxn];
ll maxflow,ans;   //最大流 和 费用
int vis[maxn],pre[maxn];

bool spfa(){
    queue<int >qu;
    for(int i=0;i<indexx+17;i++){
        dis[i]=INF;
        vis[i]=0;
    }
    qu.push(s);
    dis[s]=0;
    vis[s]=1;
    incf[s]=INF;
    pre[t]=-1;
    while(!qu.empty()){
        int x=qu.front();
        vis[x]=0;
        qu.pop();
        for(int i=head[x];i;i=edge[i].next){
            if(!edge[i].vul){
                continue;
            }
            int y=edge[i].to;
            if(dis[y]>dis[x]+edge[i].cost){
                dis[y]=dis[x]+edge[i].cost;
                incf[y]=min(incf[x],edge[i].vul);
                pre[y]=i;
                if(!vis[y]){
                    vis[y]=1;
                    qu.push(y);
                }
            }

        }
    }
    if(pre[t]==-1){
        return false;
    }
    return true;
}
void update(){
    int x=t;
    while(x!=s){
        int i=pre[x];
        edge[i].vul-=incf[t];
        edge[i^1].vul+=incf[t];
        x=edge[i^1].to;
    }
    maxflow+=incf[t];
    ans+=dis[t]*incf[t];
}

ll deal2(){    // 费用流
    ans=0;
    while(spfa()){
        update();
    }
    return ans;
}

void dfs(int x1,int x2){
  //  printf("%d -- --  %d  \n",x1,x2);
    if(G[1][x1].size()==0){
        if(x1==x2){
            dp[x1][x2]=0;
        }else{
            dp[x1][x2]=1;
        }
   //     printf("@@  \n");
        return ;
    }
    for(int i=0;i<G[1][x1].size();i++){

        int y1=G[1][x1][i];
        for(int j=0;j<G[2][x2].size();j++){

            int y2=G[2][x2][j];

   //         printf("%d  ******  %d    $$      %d  %d  \n",y1,y2,x1,x2);
            if(zxbs[1][y1]==zxbs[2][y2]){
                dfs(y1,y2);
            }
        }
    }

    indexx=0;

    for(int i=0;i<G[1][x1].size();i++){
        int y1=G[1][x1][i];
        dia[y1]=++indexx;
    }

    for(int i=0;i<G[2][x2].size();i++){
        int y2=G[2][x2][i];
        dia[y2+n]=++indexx;
    }
    s=0,t=++indexx;

    init(indexx);

    // 建边

    for(int i=0;i<G[1][x1].size();i++){
        int y1=G[1][x1][i];

        for(int j=0;j<G[2][x2].size();j++){

            int y2=G[2][x2][j];

            if(zxbs[1][y1]==zxbs[2][y2]){
                add(dia[y1],dia[y2+n],1,dp[y1][y2]);
            }


        }
    }
    for(int i=0;i<G[1][x1].size();i++){
        int y1=G[1][x1][i];
        add(s,dia[y1],1,0);
    }
     for(int i=0;i<G[2][x2].size();i++){
        int y2=G[2][x2][i];
        add(dia[y2+n],t,1,0);
    }

    dp[x1][x2]=deal2();

    if(x1!=x2){
        dp[x1][x2]++;
    }
  //  printf("%d   &&&   %d   %lld \n",x1,x2,dp[x1][x2]);

}

int main(){

    scanf("%d",&n);
    int x;
    int root1,root2;
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        if(x==0){
            root1=i;
            continue;
        }
        G[1][x].pb(i);
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        if(x==0){
            root2=i;
            continue;
        }
        G[2][x].pb(i);
    }

    getzxbs(root1,1);
    getzxbs(root2,2);

    dfs(root1,root2);

    printf("%lld\n",dp[root1][root2]);

}

/*
11
10 1 1 5 10 7 4 5 11 0 3
10 1 7 11 10 4 9 5 5 0 1

输出: 5
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值