【题目描述】:
树是一种很常见的数据结构。
我们把N个点,N-1条边的连通无向图称为树。
若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树。
对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相同,那么这两个树是同构的。也就是说,它们具有相同的形态。现在,给你M个有根树,请你把它们按同构关系分成若干个等价类。
【题目解法】:
首先很容易想到枚举每棵树的每个点作为根,然后判断是否重构
现在问题转化为判断两个有根树是否重构
解法一:最小表示法。我们用括号序列表示一棵树,每次我们求以一个点为根的子树的最小表示,我们可以划分为求其儿子的最小表示,然后把这些括号序列排序再串起来,两边补一个()即可。然后我们可以对每一颗树求出最小字典序的最小表示法,然后判断即可
解法二:hash。其实和上面差不多,只不过需要一个比较好的hash函数即可
复杂度O(N^3)
下面代码中给出了两种解法
其中hash快一点
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;
const int N=55;
struct edge {int x,next;} b[N*2];
int m,n,tot,a[N];
inline long long getint()
{
long long x=0; char c=getchar(); bool flag=false;
while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
if (c=='-') flag=true,c=getchar();
while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
if (flag) return -x; else return x;
}
inline void addedge(int x,int y)
{
++tot;
b[tot].x=y;
b[tot].next=a[x];
a[x]=tot;
}
void init()
{
m=getint();
}
namespace MinRepresent
{
string s[N],ans[N],son[N];
void init()
{
n=getint(); tot=0; memset(a,0,sizeof(a));
for (int j=1; j<=n; j++)
{
int x=getint();
if (x!=0) addedge(j,x),addedge(x,j);
}
}
void dfs(int x,int fa)
{
s[x]='('; int cnt=0;
for (int p=a[x];p;p=b[p].next) if (b[p].x!=fa) dfs(b[p].x,x);
for (int p=a[x];p;p=b[p].next) if (b[p].x!=fa) son[++cnt]=s[b[p].x];
if (cnt>0) sort(son+1,son+cnt+1);
for (int i=1; i<=cnt; i++) s[x]+=son[i];
s[x]+=')';
}
void solve()
{
for (int i=1; i<=m; i++)
{
init();
for (int j=1; j<=n; j++)
{
dfs(j,0); if (j==1) {ans[i]=s[j]; continue;}
if (s[j]<ans[i]) ans[i]=s[j];
}
}
for (int i=1; i<=m; i++)
{
int nowans=i;
for (int j=1; j<i; j++) if (ans[i]==ans[j]) nowans=min(nowans,j);
printf("%d\n",nowans);
}
}
}
namespace Hash
{
unsigned long long H[N],hash[N],ans[N];
void init()
{
n=getint(); tot=0; memset(a,0,sizeof(a));
for (int j=1; j<=n; j++)
{
int x=getint();
if (x!=0) addedge(j,x),addedge(x,j);
}
}
void dfs(int x,int fa,int dep)
{
unsigned long long now=H[dep];
for (int p=a[x];p;p=b[p].next)
{
int pp=b[p].x; if (pp==fa) continue;
dfs(pp,x,dep+1); now=now+hash[pp]*H[dep];
}
hash[x]=now*now;
}
void solve()
{
srand(19990315);
for (int i=1; i<=50; i++) H[i]=rand();
for (int i=1; i<=m; i++)
{
init();
for (int j=1; j<=n; j++)
{
dfs(j,0,1); if (j==1) {ans[i]=hash[j]; continue;}
if (hash[j]<ans[i]) ans[i]=hash[j];
}
}
for (int i=1; i<=m; i++)
{
int nowans=i;
for (int j=1; j<i; j++) if (ans[i]==ans[j]) nowans=min(nowans,j);
printf("%d\n",nowans);
}
}
}
int main()
{
init();
Hash::solve();
return 0;
}