hackerrank HourRank 21 Hard Tree Isomorphism(无根树同构)
传送门:https://www.hackerrank.com/contests/hourrank-21/challenges/tree-isomorphism
题意是给你一颗树,求该树的子树中有多少棵不同构的树。
同构的概念见题目或者自行百度
由于是棵树,所以边数m为点数n-1,可以枚举2^m次方,用并查集(或者dfs判连通性)判断当前情况是否是一棵树,如果是就进行Hash判断是否重构。
暂时不懂这Hash判重构的原理,先记录下用法:
对于有根树,我只要从点开始跑一发hash,如果两棵树的hash值不同,说明两棵树不同构。如果是无根树,我需要判断从所有点开始跑的hash都要相同才说明两棵树不同构。所以对于无根树,我可以用set储存在该树的所有点得到的hash值,这个set就相当于该树的hash值。那么对于这题,我可以枚举所有边的情况后得到树,再得到该树的hash值,存进set中,最后set的size()+1就是答案(+1是只有一个点的情况)
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <string> #include <stack> #include <map> #include <set> #include <bitset> #define X first #define Y second #define clr(u,v); memset(u,v,sizeof(u)); #define in() freopen("data","r",stdin); #define out() freopen("ans","w",stdout); #define Clear(Q); while (!Q.empty()) Q.pop(); #define pb push_back using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 20; const int INF = 0x3f3f3f3f; struct Edge { int v, to; } E[maxn*2]; struct node { int u, v, flag; } P[maxn]; int head[maxn], cnt; void addedge(int v, int u) { E[cnt].v = u, E[cnt].to = head[v]; head[v] = cnt++; } #define mod 9973 int h[maxn];//树的最大深度等于点数-1 int vis[maxn]; int Hash(int cur, int depth)//Hash部分 { int Sum = h[depth]; vis[cur] = 1; for(int i = head[cur]; ~i; i = E[i].to) if(!vis[E[i].v]) Sum = (Sum + Hash(E[i].v, depth + 1) * h[depth]) % mod; vis[cur] = 0; return (Sum * Sum) % mod; } set <set<int> > S; int f[maxn], n, sum[maxn]; void init() { for (int i = 0; i <= n; i++) f[i] = i, sum[i] = 1; clr(head, -1); cnt = 0; } int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); } void mix(int x, int y) { int fx = find(x), fy = find(y); if (fx == fy) return ; f[fx] = fy; sum[fy] += sum[fx]; } int ind = 1; void solve() { set <int> temp;//此时树的hash值 init(); for (int i = 1; i < n; i++) if (P[i].flag) { mix(P[i].v, P[i].u); addedge(P[i].v, P[i].u); addedge(P[i].u, P[i].v); } int Sum = 0, st = 1; for (int i = 1; i <= n; i++)//利用并查集判断是否是一棵树,Sum==1说明是点数大于1的树 if (find(i) == i && sum[i] != 1) { st = i; Sum++; } if (Sum != 1) return ; for (int i = 1; i <= n; i++) if (find(i) == st) { int cur = Hash(i, 1);//cur 代表从cur这个点开始跑的hash值 temp.insert(cur); } S.insert(temp); } void dfs(int cur)//2^(n-1)枚举所有情况 { if (cur == n) { solve(); return ; } P[cur].flag = 1; dfs(cur + 1); P[cur].flag = 0; dfs(cur + 1); } int main() { scanf("%d", &n); for(int i = 0; i <= n; ++i) h[i] = rand() % mod; for (int i = 1; i < n; i++) scanf("%d%d", &P[i].u, &P[i].v); dfs(1); printf("%d\n", (int)S.size() + 1); return 0; }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~