[jzoj 4272] 【NOIP2015模拟10.28B组】序章-弗兰德的秘密{树形动态规划}

题目

Description
背景介绍
弗兰德,我不知道这个地方对我意味着什么。这里是一切开始的地方。3年前,还是个什么都没见过的少年,来到弗兰德的树下,走进了封闭的密室,扭动的封尘已久机关,在石板上知道了这个世界最角落的最阴暗的东西。那种事情,从未忘怀,从未动摇,我还记得,那一天,我,里修,第一次拔起了剑……

弗兰德的密室里,机关上方画着两棵树的字样,机关下方是一个有数字的刻度……
弗兰德最高的两棵树,只要知道两棵树的共同的相似度就行了……
给定两棵有根树,可以任意删除两棵树上的节点(删除一棵节点必须保证该节点的子树内的所有节点也必须要被删除,换一种说法,删除后的树必须联通并形成一棵树,且根节点不能被删除),使得删除后的两棵树同构,这两棵树有一个共同大小,即树的size,最大化同构的树的size即为机关的答案……

注:两棵同构的树要满足以下条件:
1、两棵树节点个数相等。
2、两棵树的以根节点的儿子为根子树对应同构。如下图,为两棵同构的有根树。
如下图,为两棵同构的有根树。
这里写图片描述

Input
一行两个整数n,m分别表示两棵有根树的大小。
以下n-1行描述第一棵树,每行两个数x,y表示x号节点是y号节点父亲。
以下m-1行描述第二棵树,每行两个数x,y表示x号节点是y号节点父亲。
数据保证两棵树的1号节点均为根。

Output
一行一个数,表示两棵树的相似度(删除后最大化的同构树的大小)。


解题思路

f[i][[j]表示第一棵树i节点与第二棵树j节点的相似值。动态转移方程为: F[x][y]=Max{F[x的儿子][y的儿子]}+1 ; 需要枚举x的儿子与y的儿子匹配,时间复杂度O(5!*n2) ; 最后答案就是F[1][1].

在状态转移的时候,我们要让x的子节点与y的子节点匹配。在程序中具体的实现就是先dfs树1,枚举x的子节点,然后再处理树2,一个个枚举y的子节点,搜索改变匹配序列


代码

#include<cstdio>
#include<algorithm>
#include<vector>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std; 
vector<int> sx[1001],sy[1001]; 
bool v[1001]; 
int f[1001][1001],n,x,y,m; 
void dfss(int k,int sum)
{
    if (k>=sx[x].size())
    { f[x][y]=max(f[x][y],sum); return; }
    dfss(k+1,sum); 
    bool p=false; 
    for (int i=0;i<sy[y].size();i++)
     if (!v[i])
     {
        p=true; 
        v[i]=true; 
        dfss(k+1,sum+f[sx[x][k]][sy[y][i]]); 
        v[i]=false; 
     }
     if (p) f[x][y]=max(f[x][y],sum); 
}
void dfs(int k)
{
    if (!sx[k].size())
    {
        for (int i=1;i<=m;i++)
         f[k][i]=1; 
         return; 
    }
    for(int i=0;i<sx[k].size();i++) 
     dfs(sx[k][i]); 
    for(int i=1;i<=m;i++)
    {
        if (!sy[i].size()) f[k][i]=1;
        else {
            x=k; y=i; 
            dfss(0,0); 
            f[k][i]++; 
        }
    }
}
int main()
{
    fre(frand); 
    scanf("%d%d",&n,&m); 
    for (int i=1;i<n;i++)
     scanf("%d%d",&x,&y),sx[x].push_back(y); 
    for (int i=1;i<m;i++)
     scanf("%d%d",&x,&y),sy[x].push_back(y); 
    dfs(1); 
    printf("%d",f[1][1]); 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值