【最小表示法\同构二叉树】等价二叉树
2012-10-28 17:32
453 查看
3、等价二叉树(tree.pas)
【题目描述】
二叉树有很多种,但是它们很多都是等价的。某二叉树通过把某非根节点作为根节点而保持其它各节点之间的关系不变,重新建立出一棵树,若这棵树是一棵二叉树,则称这两棵二叉树等价,如下图所示的三棵二叉树即为等价关系。
把第一棵树的E节点作为根则形成了第二棵树,而把第一棵树的B节点作为根则形成了第三棵树。我们现在要给出一棵基准二叉树,请你来判断给出的其他树与否和它等价。
【输入格式】
输入来自文件tree.in,第一行包含一棵基准二叉树。第二行包含一个正整数k(k≤26),表示接下来有k棵待检验的二叉树。以下k行每行包含一棵待检验的二叉树。二叉树的节点为大写字母,列出所有“节点名(其孩子节点名)”来表示一棵树,若某节点有两个孩子节点,则在“()”中用“/”分隔,若某节点没有孩子节点,则用“(-)”表示。如上图所示的第一棵树可以表示为:A(B/C)B(D)C(E/F)D(-)E(-)F(-)。表示每棵树的字符串中不包含空格。
【输出格式】
输出到文件tree.out,包含一个由Y和N组成的长度为k的字符串,表示对应位置的待检验二叉树是否和基准二叉树等价(等价则为Y,否则为N)。
【样例输入】
A(B/C)B(D)C(E/F)D(-)E(-)F(-)
3
A(B)B(E/F)E(C)F(-)C(D)D(-)
A(B/C)B(-)C(D)D(E/F)E(-)F(-)
A(B)B(C)C(D)D(E)E(F)F(-)
【样例输出】
YYN
以前看到过一篇论文,讲最小表示法在字符串中的运用,没有太看懂。做了这道题算是有点入门了。
我觉得最小表示法确实是一种很独到,很有魅力的方法。
类似于在排列中的一个例子,有m个固定顺序的数,让另外(n-m)个数随意放进来,求方法总数。
方法是(n!)/(m!),即随意排好之后,不考虑这m个数,共有m!种相同的排列,所以我们除以这m个数排列的方法总数,就固定了这m个数。
而在这道题中,用类似于归并排序的方法,根据括号的字典序来排列,比如( ( ( ) ( ) ) ( ( ) ) ) ,排序后就应该是( ( ( ) ) ( ( ( ) ( ) ) )。这样一棵树就只对应于一个括号序列。
我一开始的解决方法是先生成括号序列,然后再手动括号匹配,然后再分成多个子括号组,将子括号组排序,递归下去。
这样做很麻烦,而且枚举不同的根后生成的树不一定还是二叉树,因此处理很困难。
最好的方法是一边遍历,一边生成括号序,返回给父亲的时候,就返回已经排好序的子树的括号序,并在两边再加上一对括号。
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using std::max;
using std::string;
long n;
string bracket1[30];
long hash[30];
char source[500];
bool used[30];
struct node
{
long ind;
node* nxt;
};
node* head[30];
void insert(long u,long v)
{
node* nn = new node;
nn -> ind = v;
nn -> nxt = head[u];
head[u] = nn;
}
string dfs(long u)
{
string son[5];
long cnt = 0;
for (node* nn=head[u];nn;nn=nn->nxt)
{
long v = nn->ind;
if(!used[v])
{
used[v] = true;
cnt ++;
son[cnt] = "(";
son[cnt] += dfs(v);
used[v] = false;
}
}
sort(son+1,son+1+cnt);
for (long i=2;i<cnt+1;i++)
son[1] += son[i];
return son[1] + ")";
}
void init()
{
memset(hash,0,sizeof hash);
memset(head,0,sizeof head);
n = 0;
scanf("%s",source+1);
long i = 0;
while (source[++i])
{
if (source[i]>='A'&&source[i]<='Z')
{
long u = source[i]-'A'+1;
if (!hash[u])
u = hash[u] = ++n;
else u = hash[u];
long j = i+1;
while (source[j] && source[j]!=')')
{
if (source[j]>='A' && source[j]<='Z')
{
long v = source[j]-'A'+1;
if (!hash[v])
v = hash[v] = ++n;
else v = hash[v];
insert(u,v);
insert(v,u);
}
j ++;
}
if (!source[j]) break;
i = j;
}
}
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
init();
long n0 = n;
for (long i=1;i<n0+1;i++)
bracket1[i] = dfs(i);
long k;
scanf("%ld",&k);
for (long i=1;i<k+1;i++)
{
init();
string bracket2 = dfs(1);
bool ok = false;
for (long i=1;i<n0+1;i++)
{
if (bracket2 == bracket1[i])
{
ok = true;
break;
}
}
if (ok)
printf("Y");
else
printf("N");
}
return 0;
}
之前的方法:
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
using std::max;
bool used[50];
long fa[50];
long ls[50];
long rs[50];
long hash[50];
char tree[500];
long n = 0;
long* findbracket(long *source,long *source2)
{
long cnt = 0;
for (long* i=source;i!=source2;i++)
{
cnt += *i;
if (cnt == 0)
return i;
}
return source2;
}
int cmpr(long *l1,long *r1,long *l2,long *r2)
{
long *i;
long *j;
for (i=l1,j=l2;i!=r1&&j!=r2;i++,j++)
{
if (*i < *j) return -1;
if (*i > *j) return 1;
}
if (i == r1 && j != r2) return -1;
if (i != r1 && j == r2) return 1;
return 0;
}
long tmp[500];
void swap(long *l1,long *r1,long *l2,long *r2)
{
memcpy(tmp,l1,(r1-l1)*sizeof(long));
memcpy(l1,l2,(r2-l2)*sizeof(long));
memcpy(l1+(r2-l2),tmp,(r1-l1)*sizeof(long));
}
void treesort(long *source,long *source2)
{
if (source+2 >= source2)
return;
long mid = findbracket(source+1,source2)-source;
treesort(source+1,source+mid+1);
treesort(source+mid+1,source2-1);
if (cmpr(source+1,source+mid+1,source+1+mid,source2-1) == -1)
{
swap(source+1,source+mid+1,source+1+mid,source2-1);
}
}
void dfs1(long u,long* source)
{
if (!used[ls[u]] && ls[u])
{
used[ls[u]] = true;
source[++source[0]] = -1;
dfs1(ls[u],source);
used[ls[u]] = false;
}
if (!used[rs[u]] && rs[u])
{
used[rs[u]] = true;
source[++source[0]] = -1;
dfs1(rs[u],source);
used[rs[u]] = false;
}
if (!used[fa[u]] && fa[u])
{
used[fa[u]] = true;
source[++source[0]] = -1;
dfs1(fa[u],source);
used[fa[u]] = false;
}
source[++source[0]] = 1;
}
void dfs(long u,long* source)
{
if (ls[u])
{
source[++source[0]] = -1;
dfs(ls[u],source);
}
if (rs[u])
{
source[++source[0]] = -1;
dfs(rs[u],source);
}
source[++source[0]] = 1;
}
void init(long *source)
{
memset(ls,0,sizeof ls);
memset(rs,0,sizeof rs);
memset(fa,0,sizeof fa);
memset(hash,0,sizeof hash);
n = 0;
scanf("%s",tree+1);
long i = 0;
while (tree[++i])
{
if (tree[i]>='A'&&tree[i]<='Z')
{
long u = tree[i]-'A'+1;
if (!hash[u])
u = hash[u] = ++n;
else u = hash[u];
long j = i+1;
while (tree[j] && tree[j]!=')')
{
if (tree[j]>='A' && tree[j]<='Z')
{
long v = tree[j]-'A'+1;
if (!hash[v])
v = hash[v] = ++n;
else v = hash[u];
fa[v] = u;
if (!ls[u]) ls[u] = v;
else rs[u] = v;
}
j ++;
}
if (!tree[j]) break;
i = j;
}
}
}
long bracket1[50][500];
long bracket2[500];
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
init(bracket2);
for (long i=1;i<n+1;i++)
{
memcpy(bracket1[i],bracket2,sizeof bracket2);
used[i] = true;
bracket1[i][++bracket1[i][0]] = -1;
dfs1(i,bracket1[i]);
treesort(bracket1[i]+1,bracket1[i]+1+bracket1[i][0]);
used[i] = false;
}
long k;
scanf("%ld",&k);
while (k--)
{
memset(bracket2,0,sizeof bracket2);
init(bracket2);
long root = 0;
for (long i=1;i<n+1;i++)
{
if (!fa[i])
{
root = i;
break;
}
}
bracket2[++bracket2[0]] = -1;
dfs(root,bracket2);
treesort(bracket2+1,bracket2+1+bracket2[0]);
bool ok = false;
for (long i=1;i<n+1;i++)
{
if (cmpr(bracket1[i]+1,bracket1[i]+1+bracket1[i][0],bracket2+1,bracket2+1+bracket2[0])==0)
{
ok = true;
break;
}
}
if (ok)
printf("Y");
else
printf("N");
}
return 0;
}
【题目描述】
二叉树有很多种,但是它们很多都是等价的。某二叉树通过把某非根节点作为根节点而保持其它各节点之间的关系不变,重新建立出一棵树,若这棵树是一棵二叉树,则称这两棵二叉树等价,如下图所示的三棵二叉树即为等价关系。
把第一棵树的E节点作为根则形成了第二棵树,而把第一棵树的B节点作为根则形成了第三棵树。我们现在要给出一棵基准二叉树,请你来判断给出的其他树与否和它等价。
【输入格式】
输入来自文件tree.in,第一行包含一棵基准二叉树。第二行包含一个正整数k(k≤26),表示接下来有k棵待检验的二叉树。以下k行每行包含一棵待检验的二叉树。二叉树的节点为大写字母,列出所有“节点名(其孩子节点名)”来表示一棵树,若某节点有两个孩子节点,则在“()”中用“/”分隔,若某节点没有孩子节点,则用“(-)”表示。如上图所示的第一棵树可以表示为:A(B/C)B(D)C(E/F)D(-)E(-)F(-)。表示每棵树的字符串中不包含空格。
【输出格式】
输出到文件tree.out,包含一个由Y和N组成的长度为k的字符串,表示对应位置的待检验二叉树是否和基准二叉树等价(等价则为Y,否则为N)。
【样例输入】
A(B/C)B(D)C(E/F)D(-)E(-)F(-)
3
A(B)B(E/F)E(C)F(-)C(D)D(-)
A(B/C)B(-)C(D)D(E/F)E(-)F(-)
A(B)B(C)C(D)D(E)E(F)F(-)
【样例输出】
YYN
以前看到过一篇论文,讲最小表示法在字符串中的运用,没有太看懂。做了这道题算是有点入门了。
我觉得最小表示法确实是一种很独到,很有魅力的方法。
类似于在排列中的一个例子,有m个固定顺序的数,让另外(n-m)个数随意放进来,求方法总数。
方法是(n!)/(m!),即随意排好之后,不考虑这m个数,共有m!种相同的排列,所以我们除以这m个数排列的方法总数,就固定了这m个数。
而在这道题中,用类似于归并排序的方法,根据括号的字典序来排列,比如( ( ( ) ( ) ) ( ( ) ) ) ,排序后就应该是( ( ( ) ) ( ( ( ) ( ) ) )。这样一棵树就只对应于一个括号序列。
我一开始的解决方法是先生成括号序列,然后再手动括号匹配,然后再分成多个子括号组,将子括号组排序,递归下去。
这样做很麻烦,而且枚举不同的根后生成的树不一定还是二叉树,因此处理很困难。
最好的方法是一边遍历,一边生成括号序,返回给父亲的时候,就返回已经排好序的子树的括号序,并在两边再加上一对括号。
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using std::max;
using std::string;
long n;
string bracket1[30];
long hash[30];
char source[500];
bool used[30];
struct node
{
long ind;
node* nxt;
};
node* head[30];
void insert(long u,long v)
{
node* nn = new node;
nn -> ind = v;
nn -> nxt = head[u];
head[u] = nn;
}
string dfs(long u)
{
string son[5];
long cnt = 0;
for (node* nn=head[u];nn;nn=nn->nxt)
{
long v = nn->ind;
if(!used[v])
{
used[v] = true;
cnt ++;
son[cnt] = "(";
son[cnt] += dfs(v);
used[v] = false;
}
}
sort(son+1,son+1+cnt);
for (long i=2;i<cnt+1;i++)
son[1] += son[i];
return son[1] + ")";
}
void init()
{
memset(hash,0,sizeof hash);
memset(head,0,sizeof head);
n = 0;
scanf("%s",source+1);
long i = 0;
while (source[++i])
{
if (source[i]>='A'&&source[i]<='Z')
{
long u = source[i]-'A'+1;
if (!hash[u])
u = hash[u] = ++n;
else u = hash[u];
long j = i+1;
while (source[j] && source[j]!=')')
{
if (source[j]>='A' && source[j]<='Z')
{
long v = source[j]-'A'+1;
if (!hash[v])
v = hash[v] = ++n;
else v = hash[v];
insert(u,v);
insert(v,u);
}
j ++;
}
if (!source[j]) break;
i = j;
}
}
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
init();
long n0 = n;
for (long i=1;i<n0+1;i++)
bracket1[i] = dfs(i);
long k;
scanf("%ld",&k);
for (long i=1;i<k+1;i++)
{
init();
string bracket2 = dfs(1);
bool ok = false;
for (long i=1;i<n0+1;i++)
{
if (bracket2 == bracket1[i])
{
ok = true;
break;
}
}
if (ok)
printf("Y");
else
printf("N");
}
return 0;
}
之前的方法:
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
using std::max;
bool used[50];
long fa[50];
long ls[50];
long rs[50];
long hash[50];
char tree[500];
long n = 0;
long* findbracket(long *source,long *source2)
{
long cnt = 0;
for (long* i=source;i!=source2;i++)
{
cnt += *i;
if (cnt == 0)
return i;
}
return source2;
}
int cmpr(long *l1,long *r1,long *l2,long *r2)
{
long *i;
long *j;
for (i=l1,j=l2;i!=r1&&j!=r2;i++,j++)
{
if (*i < *j) return -1;
if (*i > *j) return 1;
}
if (i == r1 && j != r2) return -1;
if (i != r1 && j == r2) return 1;
return 0;
}
long tmp[500];
void swap(long *l1,long *r1,long *l2,long *r2)
{
memcpy(tmp,l1,(r1-l1)*sizeof(long));
memcpy(l1,l2,(r2-l2)*sizeof(long));
memcpy(l1+(r2-l2),tmp,(r1-l1)*sizeof(long));
}
void treesort(long *source,long *source2)
{
if (source+2 >= source2)
return;
long mid = findbracket(source+1,source2)-source;
treesort(source+1,source+mid+1);
treesort(source+mid+1,source2-1);
if (cmpr(source+1,source+mid+1,source+1+mid,source2-1) == -1)
{
swap(source+1,source+mid+1,source+1+mid,source2-1);
}
}
void dfs1(long u,long* source)
{
if (!used[ls[u]] && ls[u])
{
used[ls[u]] = true;
source[++source[0]] = -1;
dfs1(ls[u],source);
used[ls[u]] = false;
}
if (!used[rs[u]] && rs[u])
{
used[rs[u]] = true;
source[++source[0]] = -1;
dfs1(rs[u],source);
used[rs[u]] = false;
}
if (!used[fa[u]] && fa[u])
{
used[fa[u]] = true;
source[++source[0]] = -1;
dfs1(fa[u],source);
used[fa[u]] = false;
}
source[++source[0]] = 1;
}
void dfs(long u,long* source)
{
if (ls[u])
{
source[++source[0]] = -1;
dfs(ls[u],source);
}
if (rs[u])
{
source[++source[0]] = -1;
dfs(rs[u],source);
}
source[++source[0]] = 1;
}
void init(long *source)
{
memset(ls,0,sizeof ls);
memset(rs,0,sizeof rs);
memset(fa,0,sizeof fa);
memset(hash,0,sizeof hash);
n = 0;
scanf("%s",tree+1);
long i = 0;
while (tree[++i])
{
if (tree[i]>='A'&&tree[i]<='Z')
{
long u = tree[i]-'A'+1;
if (!hash[u])
u = hash[u] = ++n;
else u = hash[u];
long j = i+1;
while (tree[j] && tree[j]!=')')
{
if (tree[j]>='A' && tree[j]<='Z')
{
long v = tree[j]-'A'+1;
if (!hash[v])
v = hash[v] = ++n;
else v = hash[u];
fa[v] = u;
if (!ls[u]) ls[u] = v;
else rs[u] = v;
}
j ++;
}
if (!tree[j]) break;
i = j;
}
}
}
long bracket1[50][500];
long bracket2[500];
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
init(bracket2);
for (long i=1;i<n+1;i++)
{
memcpy(bracket1[i],bracket2,sizeof bracket2);
used[i] = true;
bracket1[i][++bracket1[i][0]] = -1;
dfs1(i,bracket1[i]);
treesort(bracket1[i]+1,bracket1[i]+1+bracket1[i][0]);
used[i] = false;
}
long k;
scanf("%ld",&k);
while (k--)
{
memset(bracket2,0,sizeof bracket2);
init(bracket2);
long root = 0;
for (long i=1;i<n+1;i++)
{
if (!fa[i])
{
root = i;
break;
}
}
bracket2[++bracket2[0]] = -1;
dfs(root,bracket2);
treesort(bracket2+1,bracket2+1+bracket2[0]);
bool ok = false;
for (long i=1;i<n+1;i++)
{
if (cmpr(bracket1[i]+1,bracket1[i]+1+bracket1[i][0],bracket2+1,bracket2+1+bracket2[0])==0)
{
ok = true;
break;
}
}
if (ok)
printf("Y");
else
printf("N");
}
return 0;
}
相关文章推荐
- 字符串循环同构的最小表示法(转)
- poj 1635 Subway tree systems 判断树的同构 树的最大最小表示法模板
- 【字符串】【最小表示法】Vijos P1683 有根树的同构问题
- POJ 1509 循环同构的最小表示法
- 理解字符串循环同构的最小表示法
- [转]浅析“最小表示法”思想在字符串循环同构问题中的应用-HDU2609
- 字符串循环同构——最小表示法の板子
- 理解字符串循环同构的最小表示法
- HDU 2609 How many(字符串同构,最小表示法)
- POJ 3349 Snowflake Snow Snowflakes ( HASH+最小表示判同构 )
- usaco 5.5 Hidden Password(最小表示法求同构)
- 最小表示法 字符串循环同构问题
- (结构数组表示二叉树)树的同构
- [BZOJ4337][BJOI2015]树的同构(树的最小表示法)
- POJ1509:Glass Beads(循环同构最小表示)
- 字符串同构最小最大表示法模板&&manacher模板
- 字符串同构的最小表示方法
- 字符串循环同构的最小表示法(转)
- 【判段同构】字符串最小表示法
- 最小表示法 (在解决判断“同构”一类问题中有很大作用)