P5043
Solution:
题意:
给定 \(m\) 棵无根树,对于每棵树求编号与其同构的树的最小编号。
做法
这类树同构的问题,容易想到树哈希。
判断两个两棵树是否同构。
如果两棵树中的 每一个节点 的哈希值都相等,那么这两棵树同构。
注意到 \(1 \leq n, m \leq 50\),所以我们先得到每棵树所有节点的哈希值,然后直接暴力判断即可。时间复杂度约为 \(\mathcal{O}(N ^ 2 M)\)。
如何得到哈希值
因为数据较小,我们可以使用一种简单的方法。
首先,对于叶子节点,我们将哈希值设为 \(1\)。
否则对于非空节点,我们先求出其子节点的哈希值,然后将其排序,接着运用类似字符串哈希的方法,将这些子树的哈希值当做字符进行处理。
实现方式:
int hash(int u, int fa)
{
if (G[u].size() == 1 && fa != 0) return 1;
else
{
vector <int> has;//子树的哈希值
int res = 1;
for (int i = 0; i < G[u].size(); i ++ )
{
int v = G[u][i];
if (v == fa) continue;
has.push_back(hash(v, u));
}
sort(has.begin(), has.end());
for (int i = 0; i < has.size(); i ++ ) res = (res * p + has[i]) % mod;
return res;
}
}
tips
因为有多棵树,所以我们将每棵树用一个结构体存起来,这样使用会更方便。
代码
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int unsigned long long
const int N = 60, M = 50, K = 120;
const int p = 13331, mod = 1e9 + 7;
using namespace std;
//如果两棵树同构,这两棵树中每一个结点的哈希值一定是相同的。
int m, n;
struct graph
{
vector <int> G[N];
void add(int u, int v)
{
G[u].push_back(v), G[v].push_back(u);
}
int hash(int u, int fa)
{
if (G[u].size() == 1 && fa != 0) return 1;
else
{
vector <int> has;//子树的哈希值
int res = 1;
for (int i = 0; i < G[u].size(); i ++ )
{
int v = G[u][i];
if (v == fa) continue;
has.push_back(hash(v, u));
}
sort(has.begin(), has.end());
for (int i = 0; i < has.size(); i ++ ) res = (res * p + has[i]) % mod;
return res;
}
}
}tree[N];
int ha[N][N];
signed main()
{
IOS;
cin >> m;
for (int i = 1; i <= m; i ++ )
{
cin >> n;
graph& t = tree[i];
for (int j = 1; j <= n; j ++ )
{
int k;
cin >> k;
if (!k) continue;
t.add(j, k);
}
for (int j = 1; j <= n; j ++ )
{
int h = t.hash(j, 0);
ha[i][j] = h;
}
sort(ha[i] + 1, ha[i] + n + 1);
for (int j = 1; j <= i; j ++ )
{
bool ch = true;
for (int k = 1; k <= n; k ++ )
{
if (ha[j][k] != ha[i][k])
{
ch = false;
break;
}
}
if (ch)
{
cout << j << '\n';
break;
}
}
}
}