给定一个无根树,求他的子树的个数,子树也是无根树,边和点一一对应即为相等子树。
枚举子集,这样就可以枚举出每种树的构成,然后枚举根节点,dfs求出最小表示来判重。
注意,每一个子集如果是树的话,最多只能被计算一次,但是它的不同表示却都要放到set里(最小表示是对于确定根的)
然后如果他是森林的话,由于枚举子集从小到大,他的每棵树都算过了,所以没有问题
如果是在dfs中每一次都插入一次set,因为相同的子树在set中的表示可能会不一样,不好计数
必须枚举子树,把他们的表示都记录下来,才好判重
换句话说,一颗n个节点的子树,表示法应该是一个N*(1 << N)个二进制位才能表示。
树的最小表示让我想起很久之前学的一个字符串的最小表示
是用类似dfs序来进行描述的
以一个节点为父节点的子树的最小表示是:0,子树的最小表示排序,1
就这样。
#include<bits/stdc++.h>
using namespace std;
set<string> S;
bool gra[20][20];
int visit[20];
int n;
string dfs(int u,int pre)
{
string cur = "0";
vector<string> v;
for(int i = 1; i <= n; i++){
if (gra[u][i] && visit[i] && i != pre){
v.push_back(dfs(i,u));
}
}
sort(v.begin(),v.end());
for(int i = 0; i < v.size(); i++) cur = cur + v[i];
cur = cur + "1";
//cout << cur << ' ' << u << ' ' << pre << endl;
return cur;
}
int main()
{
int t;
scanf("%d",&t);
for(int cas = 1; cas <= t; cas++){
scanf("%d",&n);
memset(gra,0,sizeof(gra));
S.clear();
for(int i = 0; i < n - 1; i++){
int from,to;
scanf("%d %d",&from,&to);
gra[from][to] = 1;
gra[to][from] = 1;
}
int ans = 0;
for(int i = 1; i < (1 << n); i++){
memset(visit,0,sizeof(visit));
for(int j = 0; j < n; j++){
if (i & (1 << j)) visit[j + 1] = 1;
}
//for(int j = 1; j <= n; j++) printf("%d ",visit[j]);
//cout << endl;
int flag = 0;
for(int j = 0; j < n; j++){
if (i & (1 << j)) {
string ss = dfs(j + 1,-1);
//cout << ss << endl;
if (S.find(ss) == S.end()){
flag = 1;
S.insert(ss);
}
}
}
ans = ans + flag;
}
printf("Case #%d: %d\n",cas,ans);
}
}