当树为无根树时,可以枚举每一的树的节点作为根,然后判相应的有根树的同构了.
这样问题就是怎么判断两个有根树是否同构.
解法1 : 暴力枚举,就是对于两棵树,递归的比较两个数的子树,如果存在两个树的子树之间的某一个一一配对,使所有相应的子树都同构,那么这两棵树同构.
附代码:[POJ_1635]


1
//
树的最小表示,以及判定有根树是否同构
2 // 其实就是构造出来树,然后再递归的比较一下,或许是数据弱
3 #include < iostream >
4 #include < cstring >
5 #include < algorithm >
6 #include < climits >
7 #include < cstdio >
8 #include < cmath >
9
10 using namespace std;
11
12 int tree1[ 1510 ][ 1510 ];
13 int tree2[ 1510 ][ 1510 ];
14 int len1[ 1510 ];
15 int len2[ 1510 ];
16 char str1[ 3100 ],str2[ 3100 ];
17 int stk[ 1510 ];
18 int cnt1[ 1510 ], cnt2[ 1510 ];
19 bool vis[ 1510 ];
20
21 inline bool cmp1( int a, int b)
22 {
23 if (cnt1[a] != cnt1[b])
24 return cnt1[a] < cnt1[b];
25
26 return len1[a] < len1[b];
27 }
28
29 inline bool cmp2( int a, int b)
30 {
31 if (cnt2[a] != cnt2[b])
32 return cnt2[a] < cnt2[b];
33 return len2[a] < len2[b];
34 }
35
36 void DFS1( int x)
37 {
38 cnt1[x] = 1 ;
39 for ( int i = 0 ; i < len1[x]; i ++ )
40 {
41 DFS1(tree1[x][i]);
42 cnt1[x] += cnt1[tree1[x][i]];
43 }
44 }
45 void DFS2( int x)
46 {
47 cnt2[x] = 1 ;
48 for ( int i = 0 ; i < len2[x]; i ++ )
49 {
50 DFS2(tree2[x][i]);
51 cnt2[x] += cnt2[tree2[x][i]];
52 }
53 }
54
55 void build()
56 {
57 int l = strlen(str1);
58 memset(len1, 0 , sizeof (len1));
59 memset(len2, 0 , sizeof (len2));
60 int cur = 0 ;
61 int cnt = 0 ;
62 int top = 0 ;
63 for ( int i = 0 ; i < l; i ++ )
64 {
65 switch (str1[i])
66 {
67 case ' 0 ' :
68 {
69 stk[ ++ top] = cur;
70 tree1[cur][len1[cur] ++ ] = ++ cnt;
71 cur = cnt;
72 break ;
73 }
74 case ' 1 ' :
75 {
76 cur = stk[top -- ];
77 break ;
78 }
79 }
80 }
81 cur = 0 ;
82 cnt = 0 ;
83 top = 0 ;
84 for ( int i = 0 ; i < l; i ++ )
85 {
86 switch (str2[i])
87 {
88 case ' 0 ' :
89 {
90 stk[ ++ top] = cur;
91 tree2[cur][len2[cur] ++ ] = ++ cnt;
92 cur = cnt;
93 break ;
94 }
95 case ' 1 ' :
96 {
97 cur = stk[top -- ];
98 break ;
99 }
100 }
101 }
102 memset(cnt1, 0 , sizeof (cnt1));
103 memset(cnt2, 0 , sizeof (cnt2));
104 DFS1( 0 );
105 DFS2( 0 );
106 }
107
108 bool CMP( int a, int b)
109 {
110 if (len1[a] != len2[b] || cnt1[a] != cnt2[b])
111 {
112 return false ;
113 }
114 int x, y;
115
116 for ( int i = 0 ; i < len1[a]; i ++ )
117 {
118 bool bingo = false ;
119 x = tree1[a][i];
120 for ( int j = 0 ; j < len2[b]; j ++ )
121 {
122 y = tree2[b][j];
123 if (vis[y])
124 continue ;
125 if (len1[x] != len2[y] || cnt1[x] != cnt2[y])
126 continue ;
127 if (CMP(x, y))
128 {
129 vis[y] = true ;
130 bingo = true ;
131 break ;
132 }
133 }
134 if ( ! bingo)
135 {
136 for ( int j = 0 ; j < len2[b]; j ++ )
137 {
138 vis[tree2[b][j]] = false ;
139 }
140 return false ;
141 }
142 }
143 for ( int j = 0 ; j < len2[b]; j ++ )
144 {
145 vis[tree2[b][j]] = false ;
146 }
147 return true ;
148 }
149
150 int main()
151 {
152 freopen( " input.txt " , " r " , stdin);
153
154 int T;
155 gets(str1);
156 sscanf(str1, " %d " , & T);
157 while (T -- )
158 {
159 gets(str1);
160 gets(str2);
161 if (strlen(str1) != strlen(str2))
162 {
163 puts( " different " );
164 continue ;
165 }
166
167 build();
168 memset(vis, false , sizeof (vis));
169 if (CMP( 0 , 0 ))
170 {
171 puts( " same " );
172 }
173 else
174 {
175 puts( " different " );
176 }
177 }
178 return 0 ;
179 }
2 // 其实就是构造出来树,然后再递归的比较一下,或许是数据弱
3 #include < iostream >
4 #include < cstring >
5 #include < algorithm >
6 #include < climits >
7 #include < cstdio >
8 #include < cmath >
9
10 using namespace std;
11
12 int tree1[ 1510 ][ 1510 ];
13 int tree2[ 1510 ][ 1510 ];
14 int len1[ 1510 ];
15 int len2[ 1510 ];
16 char str1[ 3100 ],str2[ 3100 ];
17 int stk[ 1510 ];
18 int cnt1[ 1510 ], cnt2[ 1510 ];
19 bool vis[ 1510 ];
20
21 inline bool cmp1( int a, int b)
22 {
23 if (cnt1[a] != cnt1[b])
24 return cnt1[a] < cnt1[b];
25
26 return len1[a] < len1[b];
27 }
28
29 inline bool cmp2( int a, int b)
30 {
31 if (cnt2[a] != cnt2[b])
32 return cnt2[a] < cnt2[b];
33 return len2[a] < len2[b];
34 }
35
36 void DFS1( int x)
37 {
38 cnt1[x] = 1 ;
39 for ( int i = 0 ; i < len1[x]; i ++ )
40 {
41 DFS1(tree1[x][i]);
42 cnt1[x] += cnt1[tree1[x][i]];
43 }
44 }
45 void DFS2( int x)
46 {
47 cnt2[x] = 1 ;
48 for ( int i = 0 ; i < len2[x]; i ++ )
49 {
50 DFS2(tree2[x][i]);
51 cnt2[x] += cnt2[tree2[x][i]];
52 }
53 }
54
55 void build()
56 {
57 int l = strlen(str1);
58 memset(len1, 0 , sizeof (len1));
59 memset(len2, 0 , sizeof (len2));
60 int cur = 0 ;
61 int cnt = 0 ;
62 int top = 0 ;
63 for ( int i = 0 ; i < l; i ++ )
64 {
65 switch (str1[i])
66 {
67 case ' 0 ' :
68 {
69 stk[ ++ top] = cur;
70 tree1[cur][len1[cur] ++ ] = ++ cnt;
71 cur = cnt;
72 break ;
73 }
74 case ' 1 ' :
75 {
76 cur = stk[top -- ];
77 break ;
78 }
79 }
80 }
81 cur = 0 ;
82 cnt = 0 ;
83 top = 0 ;
84 for ( int i = 0 ; i < l; i ++ )
85 {
86 switch (str2[i])
87 {
88 case ' 0 ' :
89 {
90 stk[ ++ top] = cur;
91 tree2[cur][len2[cur] ++ ] = ++ cnt;
92 cur = cnt;
93 break ;
94 }
95 case ' 1 ' :
96 {
97 cur = stk[top -- ];
98 break ;
99 }
100 }
101 }
102 memset(cnt1, 0 , sizeof (cnt1));
103 memset(cnt2, 0 , sizeof (cnt2));
104 DFS1( 0 );
105 DFS2( 0 );
106 }
107
108 bool CMP( int a, int b)
109 {
110 if (len1[a] != len2[b] || cnt1[a] != cnt2[b])
111 {
112 return false ;
113 }
114 int x, y;
115
116 for ( int i = 0 ; i < len1[a]; i ++ )
117 {
118 bool bingo = false ;
119 x = tree1[a][i];
120 for ( int j = 0 ; j < len2[b]; j ++ )
121 {
122 y = tree2[b][j];
123 if (vis[y])
124 continue ;
125 if (len1[x] != len2[y] || cnt1[x] != cnt2[y])
126 continue ;
127 if (CMP(x, y))
128 {
129 vis[y] = true ;
130 bingo = true ;
131 break ;
132 }
133 }
134 if ( ! bingo)
135 {
136 for ( int j = 0 ; j < len2[b]; j ++ )
137 {
138 vis[tree2[b][j]] = false ;
139 }
140 return false ;
141 }
142 }
143 for ( int j = 0 ; j < len2[b]; j ++ )
144 {
145 vis[tree2[b][j]] = false ;
146 }
147 return true ;
148 }
149
150 int main()
151 {
152 freopen( " input.txt " , " r " , stdin);
153
154 int T;
155 gets(str1);
156 sscanf(str1, " %d " , & T);
157 while (T -- )
158 {
159 gets(str1);
160 gets(str2);
161 if (strlen(str1) != strlen(str2))
162 {
163 puts( " different " );
164 continue ;
165 }
166
167 build();
168 memset(vis, false , sizeof (vis));
169 if (CMP( 0 , 0 ))
170 {
171 puts( " same " );
172 }
173 else
174 {
175 puts( " different " );
176 }
177 }
178 return 0 ;
179 }
解法2:hash,算法描述见07年国家集训队论文-杨弋《Hash在信息学竞赛中的一类应用》
附代码:[POJ_1635]


1
#include
<
iostream
>
2 #include < fstream >
3 #include < algorithm >
4 #include < cstring >
5 #include < climits >
6 #include < cmath >
7
8 using namespace std;
9
10 int h[ 11000 ];
11 char str1[ 3100 ], str2[ 3100 ];
12 char * p;
13
14 int Hash( int j)
15 {
16 int sum = h[j + 5000 ]; // 这里的j是记录的节点度
17 while ( * p && * p ++ == ' 0 ' ) // 这个巧妙的循环,把子节点的hash值都加给了父节点,作为父节点的hash值
18 {
19 sum = (sum + h[j] * Hash(j + 1 )) % 19001 ;
20 }
21 return (sum * sum) % 19001 ;
22 }
23
24 inline void init()
25 {
26 for ( int i = 0 ; i < 10000 ; i ++ )
27 h[i] = (rand() % 19901 );
28 }
29
30 int main()
31 {
32 // freopen("input.txt", "r", stdin);
33 int T;
34 scanf( " %d " , & T);
35 init();
36 while (T -- )
37 {
38 scanf( " %s%s " , str1, str2);
39 p = str1;
40 int a = Hash( 1 );
41 p = str2;
42 int b = Hash( 1 );
43
44 if (a == b)
45 {
46 puts( " same " );
47 }
48 else
49 {
50 puts( " different " );
51 }
52 }
53 return 0 ;
54 }
2 #include < fstream >
3 #include < algorithm >
4 #include < cstring >
5 #include < climits >
6 #include < cmath >
7
8 using namespace std;
9
10 int h[ 11000 ];
11 char str1[ 3100 ], str2[ 3100 ];
12 char * p;
13
14 int Hash( int j)
15 {
16 int sum = h[j + 5000 ]; // 这里的j是记录的节点度
17 while ( * p && * p ++ == ' 0 ' ) // 这个巧妙的循环,把子节点的hash值都加给了父节点,作为父节点的hash值
18 {
19 sum = (sum + h[j] * Hash(j + 1 )) % 19001 ;
20 }
21 return (sum * sum) % 19001 ;
22 }
23
24 inline void init()
25 {
26 for ( int i = 0 ; i < 10000 ; i ++ )
27 h[i] = (rand() % 19901 );
28 }
29
30 int main()
31 {
32 // freopen("input.txt", "r", stdin);
33 int T;
34 scanf( " %d " , & T);
35 init();
36 while (T -- )
37 {
38 scanf( " %s%s " , str1, str2);
39 p = str1;
40 int a = Hash( 1 );
41 p = str2;
42 int b = Hash( 1 );
43
44 if (a == b)
45 {
46 puts( " same " );
47 }
48 else
49 {
50 puts( " different " );
51 }
52 }
53 return 0 ;
54 }
解法3:这种解法是从网上看到的,思路:一棵树统计每个节点的子节点个数,每个节点的深度,最后给每个节点排序,即可当成树的最小表示。最小表示相同的树同构。
但是这个解法是有错误的,已经被GG牛给cha掉了,
附上被cha的代码:[POJ_1635]


1
#include
<
iostream
>
2 using namespace std;
3 const int maxn = 3005 ;
4 struct node
5 {
6 int depth,son;
7 }tree[maxn],tree2[maxn];
8 int CMP( const void * a, const void * b)
9 {
10 node * p = (node * )a;
11 node * q = (node * )b;
12 if (p -> depth == q -> depth) return p -> son - q -> son;
13 else return p -> depth - q -> depth;
14 }
15 int min_pre( char str[],node result[]) // 无回路的图,选定根节点,即可确定一棵树。一棵树统计每个节点的子节点个数,每个节点的深度,最后排序,即可当成树的最小表示。最小表示相同的树同构。
16 {
17 int node_num = 1 ,now = 0 ;
18 int father[maxn];
19 father[ 0 ] = result[ 0 ].son = result[ 0 ].depth = 0 ;
20 for ( int i = 0 ;str[i];i ++ )
21 {
22 if (str[i] == ' 0 ' )
23 {
24 father[node_num] = now;
25 result[node_num].depth = result[father[node_num]].depth + 1 ;
26 result[node_num].son = 0 ;
27 now = node_num ++ ;
28 }
29 else
30 {
31 result[father[now]].son += result[now].son + 1 ;
32 now = father[now];
33 }
34 }
35 qsort(result,node_num, sizeof (result[ 0 ]),CMP);
36 return node_num;
37 }
38 int main()
39 {
40 char str[maxn];
41 int T;
42 scanf( " %d " , & T);
43 for ( int t = 0 ;t < T;t ++ )
44 {
45 scanf( " %s " ,str);
46 int num = min_pre(str,tree);
47 scanf( " %s " ,str);
48 int num2 = min_pre(str,tree2);
49 if (num != num2)
50 {
51 printf( " different\n " );
52 continue ;
53 }
54 int i;
55 for (i = 0 ;i < num;i ++ )
56 {
57 if (tree[i].depth != tree2[i].depth || tree[i].son != tree2[i].son)
58 {
59 printf( " different\n " );
60 break ;
61 }
62 }
63 if (i >= num) printf( " same\n " );
64 }
65 return 0 ;
66 }
2 using namespace std;
3 const int maxn = 3005 ;
4 struct node
5 {
6 int depth,son;
7 }tree[maxn],tree2[maxn];
8 int CMP( const void * a, const void * b)
9 {
10 node * p = (node * )a;
11 node * q = (node * )b;
12 if (p -> depth == q -> depth) return p -> son - q -> son;
13 else return p -> depth - q -> depth;
14 }
15 int min_pre( char str[],node result[]) // 无回路的图,选定根节点,即可确定一棵树。一棵树统计每个节点的子节点个数,每个节点的深度,最后排序,即可当成树的最小表示。最小表示相同的树同构。
16 {
17 int node_num = 1 ,now = 0 ;
18 int father[maxn];
19 father[ 0 ] = result[ 0 ].son = result[ 0 ].depth = 0 ;
20 for ( int i = 0 ;str[i];i ++ )
21 {
22 if (str[i] == ' 0 ' )
23 {
24 father[node_num] = now;
25 result[node_num].depth = result[father[node_num]].depth + 1 ;
26 result[node_num].son = 0 ;
27 now = node_num ++ ;
28 }
29 else
30 {
31 result[father[now]].son += result[now].son + 1 ;
32 now = father[now];
33 }
34 }
35 qsort(result,node_num, sizeof (result[ 0 ]),CMP);
36 return node_num;
37 }
38 int main()
39 {
40 char str[maxn];
41 int T;
42 scanf( " %d " , & T);
43 for ( int t = 0 ;t < T;t ++ )
44 {
45 scanf( " %s " ,str);
46 int num = min_pre(str,tree);
47 scanf( " %s " ,str);
48 int num2 = min_pre(str,tree2);
49 if (num != num2)
50 {
51 printf( " different\n " );
52 continue ;
53 }
54 int i;
55 for (i = 0 ;i < num;i ++ )
56 {
57 if (tree[i].depth != tree2[i].depth || tree[i].son != tree2[i].son)
58 {
59 printf( " different\n " );
60 break ;
61 }
62 }
63 if (i >= num) printf( " same\n " );
64 }
65 return 0 ;
66 }
cha的数据:
1
00101011000111
00100111001011
然后我修改了一下节点深度的设置,又被cha掉,至此死心.
反思是,这个算法是变相的hash,但是准确程度远不及算法2.
Thx to GG牛.