树同构简单Hash法

DESCRIPTION

给出两棵节点数相同的树,求其是否同构,还要输出可行解(节点的匹配关系)带spj。

input

16(n个节点)
12 4
4 13
13 5
13 14
4 8
8 3
8 6
3 15
12 10
15 1
4 9
4 2
2 7
12 16
1 11


7 6
6 2
2 10
2 11
6 9
9 14
9 8
14 4
7 12
4 15
6 1
6 16
16 13
7 5
15 3

output

YES
15 16 14 6 10 8 13 9 1 12 3 7 2 11 4 5(p[i]表示树I中节点i对应树II中的节点p[i])。

SOLUTION:

Ⅰ给出的是无根树->找树的重心或中心,以之作根(O(n)->O(2))我找的树的中心,最多两个中心。

Ⅱ设定HASH函数,我设的是h(x) = Σ(h(x.son)) * soncnt;即其儿子的HASH函数之和乘上儿子的个数。

Ⅲ输出解:从两个树的根开始一起DFS,每找到一对HASH值相同的点,就将两个点的所有儿子分别加入两个优先队列,以HASH值为关键字维护。然后再一对一对的取出来(两个点HASH值相同代表两个点为根的子树一毛一样,所以随便匹配就好)用ans记录,然后递归DFS这对刚取出的新点。


CODE:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long int
const ll bv = 9973;
const ll mod = 1e9 + 7;
const int inf = 1 << 30;
const int maxn = 100000;
using namespace std;
 
struct node{
    int v, hs;
    node(){}
    node(int a, int b){
        v = a, hs = b;
    }
    bool operator < (const node& a)const{
        return hs < a.hs;
    }
};
 
struct Edge{
    int v, Next;
}edge[maxn * 2 + 5][2];

int n, x, y, adj[maxn + 5][2], ecnt[2], f[maxn + 5], pa[maxn + 5];
int mid[3][3], ans[maxn + 5];
ll hv[maxn + 5];
int trv[maxn + 5][2];
bool mfg;
  
void HashPre()
{
    hv[0] = 1;
    for(int i = 1; i <= n; i ++){
        hv[i] = hv[i - 1] * bv % mod;
    }
}
  
void addedge(int u, int v, int fg)
{
    edge[++ ecnt[fg]][fg].v = v;
    edge[ecnt[fg]][fg].Next = adj[u][fg];
    adj[u][fg] = ecnt[fg];
}
  
void dfs(int cur, int fa, int fg)
{
    pa[cur] = fa;
    for(int p = adj[cur][fg]; p; p = edge[p][fg].Next){
        int v = edge[p][fg].v;
        if(fa == v) continue;
        f[v] = f[cur] + 1;
        dfs(v, cur, fg);
    }
}
  
void searchKP(int fg)//mid point
{
    int maxx = 0, tr;
    f[1] = 0;
    dfs(1, -1, fg);
    for(int i = 1; i <= n; i ++)
        if(f[i] > maxx)
            maxx = f[i], tr = i;
    f[tr] = 0;
    dfs(tr, -1, fg);
    maxx = 0;
    int tr2;
    for(int i = 1; i <= n; i ++)
        if(f[i] > maxx)
            maxx = f[i], tr2 = i;
    int Mid = tr2, last;
    while(f[pa[Mid]] >= maxx / 2)
        last = Mid, Mid = pa[Mid];
    mid[fg][0] = Mid;
    if(maxx & 1)//2 midpoints
        mfg = 1, mid[fg][1] = last;
}
  
ll slv(int rt, int tfg, int fa)
{
    ll ret = 1;
    int soncnt = 1;
    for(int p = adj[rt][tfg]; p; p = edge[p][tfg].Next){
        int v = edge[p][tfg].v;
        if(fa == v) continue;
        ret += slv(v, tfg, rt);
		if(ret >= mod) ret -= mod;
        soncnt ++;
    }
    return trv[rt][tfg] = ret * hv[soncnt] % mod;
}
 
void DfsAns(int r1, int fa1, int r2, int fa2)
{
    priority_queue<node> q1, q2;//match the point
	for(int p = adj[r1][0]; p; p = edge[p][0].Next){
        int v = edge[p][0].v;
        if(fa1 == v) continue;
        q1.push(node(v, trv[v][0]));
    }
    for(int p = adj[r2][1]; p; p = edge[p][1].Next){
        int v = edge[p][1].v;
        if(fa2 == v) continue;
        q2.push(node(v, trv[v][1]));
    }
    while(!q1.empty()){
        node cur1 = q1.top(), cur2 = q2.top();
        q1.pop(), q2.pop();
        ans[cur1.v] = cur2.v;
        DfsAns(cur1.v, r1, cur2.v, r2);
    }
}
 
void PrintAns(int m1, int m2)
{
    ans[m1] = m2;
    DfsAns(m1, -1, m2, -1);
    for(int i = 1; i < n; i ++)
        printf("%d ", ans[i]);
    printf("%d", ans[n]);
}
 
int main()
{
    scanf("%d", &n);
    HashPre();
    for(int i = 1; i < n; i ++){
        scanf("%d%d", &x, &y);
        addedge(x, y, 0);
        addedge(y, x, 0);
    }
    for(int i = 1; i < n; i ++){
        scanf("%d%d", &x, &y);
        addedge(x, y, 1);
        addedge(y, x, 1);
    }
    searchKP(0);
    searchKP(1);
    ll h1 = slv(mid[0][0], 0, -1);
    ll h2 = slv(mid[1][0], 1, -1);
    if(h1 == h2){
        puts("YES");
        PrintAns(mid[0][0], mid[1][0]);
        return 0;
    }
    if(mfg){//try another midpoint
        h1 = slv(mid[0][1], 0, -1);
        if(h1 == h2){
            puts("YES");
            PrintAns(mid[0][1], mid[1][0]);
            return 0;
        }
    }
    puts("NO");
    return 0;
}



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值