[关闭]
@y20070316 2017-03-21T11:34:40.000000Z 字数 1930 阅读 1629

【xsy1319】树的同构 Hash

Hash 题目


题意

树是一种很常见的数据结构。
我们把 个点, 条边的连通无向图称为树。
若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树。
对于两个树 ,如果能够把树 的所有点重新标号,使得树 和树 完全相
同,那么这两个树是同构的。也就是说,它们具有相同的形态。
现在,给你 个有根树,请你把它们按同构关系分成若干个等价类。

分析

问两棵树是否同构。
直接判断的复杂度为 ,会TLE的。
所以肯定是用一些比较不同的必然条件来判断。

我们想到了....
Hash!?
考虑构造哈希函数 ,通过Hash函数快速判断两棵树是否长一样。
怎样有效地设计Hash,刻画两棵树的形态呢?

首先,如果把无根树变成有根树,肯定会好一点。
所以,每棵无根树变成 棵有根树,对应 个Hash值。
两棵树同构,当且仅当存在至少一个共同的Hash值。

其实也可以从重心的角度出发。
找到重心,把重心和重心周围的一圈作为根,这样可以优化复杂度。

设计的Hash,不能涉及到树的儿子的先后顺序,但要考虑到儿子。
所以怎么设计其实都可以。
我的设计(来自以前的题解):

其中 是随机生成的确定值。

思路梳理

同构,这样的问题在判断是否等于等于与不等于、是否存在这两种问题,通常用Hash就搞掂了。

要体现树的形态,首先这是一棵无根树,所以要转化为有根树来判断。可以枚举每一个节点作为根,也可以找到重心,并枚举它周围的一圈。

Hash函数的设计,不能体现儿子的先后顺序,但是深度是确定的,对每一个深度确定两个随机值,Hash函数中嵌入随机值和儿子的情况就好了。

这种同构问题,通常的瓶颈在于判断同构的时间过长。
这时候我们就要用当前这个结构的某些象征来进行判断。
常用Hash,或者最小表示法之类的。

代码

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cstdlib>
  4. #include <ctime>
  5. #include <cctype>
  6. #include <algorithm>
  7. #include <map>
  8. using namespace std;
  9. #define mp(i,j) make_pair(i,j)
  10. typedef unsigned long long ull;
  11. typedef pair<ull,ull> pint;
  12. const int N=64;
  13. int m;
  14. int n;
  15. struct Edge
  16. {
  17. int v,nxt;
  18. }mp[N<<1];
  19. int tt,hd[N];
  20. ull h1[2][N];
  21. ull h2[2][N];
  22. map<pint,int> p;
  23. inline int rd(void)
  24. {
  25. int x=0; char c=getchar();
  26. for (;!isdigit(c);c=getchar());
  27. for (;isdigit(c);c=getchar()) x=x*10+c-'0';
  28. return x;
  29. }
  30. inline int add(int u,int v)
  31. {
  32. mp[++tt].v=v;
  33. mp[tt].nxt=hd[u];
  34. hd[u]=tt;
  35. }
  36. ull dfs(int t,int now,int pre,int dep)
  37. {
  38. ull ret=h1[t][dep];
  39. for (int k=hd[now];k;k=mp[k].nxt)
  40. if (mp[k].v!=pre)
  41. ret+=dfs(t,mp[k].v,now,dep+1)*h2[t][dep];
  42. return ret*ret;
  43. }
  44. int main(void)
  45. {
  46. // freopen("c.in","r",stdin);
  47. // freopen("c.out","w",stdout);
  48. srand(time(0));
  49. for (int t=0;t<=1;t++)
  50. for (int i=1;i<N;i++)
  51. h1[t][i]=rand(),h2[t][i]=rand();
  52. int res; m=rd();
  53. for (int t=1;t<=m;t++)
  54. {
  55. res=t;
  56. tt=0;
  57. memset(hd,0,sizeof hd);
  58. int x; n=rd();
  59. for (int i=1;i<=n;i++)
  60. {
  61. x=rd();
  62. if (x) add(i,x),add(x,i);
  63. }
  64. ull d0,d1; pint d;
  65. for (int rt=1;rt<=n;rt++)
  66. {
  67. d0=dfs(0,rt,0,1);
  68. d1=dfs(1,rt,0,1);
  69. d=mp(d0,d1);
  70. if (p[d]==t) continue;
  71. if (!p[d])
  72. p[d]=t;
  73. else res=min(res,p[d]);
  74. }
  75. printf("%d\n",res);
  76. }
  77. return 0;
  78. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注