有根树的同构

有根数的同构

题目大意

主要是判断给你的k棵有根树,问哪几个树是同构的

发一下原题吧:
图的同构是指两个图“相同”,这有着广泛的应用。比如当我们要对一批图施行某种操作的时候,如果我们能发现其中一些图是同构的,我们就可以在这些同构的图中只保留一个,从而降低工作量。比如,上图中的T1和T3就是同构的。
下面我们给出图的同构的定义。给出两个图G1=(V1,E1),G2=(V2,E2)。如果存在一个V1到V2的一一映射f,使得(x,y)是G1的边当且仅当(f(x),f(y))是G2的边,则称G1和G2是同构的。也就是说,我们只关心顶点间的拓扑关系而不关心顶点的编号。
任意图的同构的判定尚没有有效的算法,但要判断两棵树是否同构则要容易一点。下面我们仅考虑有根树(即树形图:有向图,存在一个根,入度为0,从根到其他任一顶点恰有一条有向路)。给出K棵有根树T1,T2,…Tk,每棵树都有n个顶点,你的任务是求出这些树在同构关系下的所有等价类(如果两棵树同构,则它们属于同一个等价类)。

Input

输入第一行包含两个整数k(1<=k<=100)和n(1<=n<=50),表示总共有k棵树,每棵都是n个顶点。接下来k行,每行描述一棵树。每行包含n-1对整数,表示这棵树的n-1条有向边。树的编号和在数据中出现的顺序一致,也就是说输入文件中第二行描述的是T1,第三行描述的是T2,…,第k+1行描述的是Tk。

Output

把给出的K棵树划分为不同的等价类,使得同一等价类中任意两棵树同构。对于每个等价类,从小到大输出这个等价类中的树的编号,用等号隔开。如果有m个等价类,则按字典序输出,每个一行。例如,有4个等价类:{4,2,7},{5,1,3},{8,9},{6},则输出:
1=3=5
2=4=7
6
8=9
注意数字和等号之间不要有空格。行首和行末可以有空格。

样例输入

3 7
7 2 7 1 7 6 2 3 1 4 6 5
7 2 7 1 2 3 1 4 1 5 5 6
4 3 3 2 4 1 1 7 5 6 4 5

样例输出

1=3
2

解题思路

可以将每一个点的子树所有节点(包括本身)记录下来,再排序。序列完全一样的子树就同构。

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int k,n,x,y;
int a[207],s[207][207];//a为临时数组,s[i][1..n]表示第i棵树的序列
int cnt,to[207],next[207],head[207];//存边
bool bz,b[107];//标志
inline void add(int x,int y){//建边
	to[++cnt]=y;
	next[cnt]=head[x];
	head[x]=cnt;
}
void dfs(int x){//深搜子树的节点数
	for(int i=head[x];i;i=next[i]){
		int t=to[i];
		dfs(t);
		a[x]+=a[t];
	}
}
inline bool cmp(int a,int b){return a>b;}//从大到小排序
void value(){//初始化
	memset(b,1,sizeof(b));
	memset(head,0,sizeof(head));
	cnt=0;
}
int main(){
	scanf("%d%d",&k,&n);
	for(int i=1;i<=k;i++){
		value();
		for(int j=1;j<n;j++)
			scanf("%d%d",&x,&y),add(x,y),a[j]=1,b[y]=false;
		//建边,a[i]为i子树的节点数(临时),每个点的初值为1,b数组用来判断根节点
		a[n]=1;//a[n]也要赋初值,原来因为这里错了
		for(int j=1;j<=n;j++)//找根节点
			if(b[j]){
				dfs(j);//深搜
				break;
			}
		sort(a+1,a+n+1,cmp);//快排
		for(int j=1;j<=n;j++)
			s[i][j]=a[j];//赋值
	}
	memset(b,1,sizeof(b));//b[i]=0/1这个树被选过没
	for(int i=1;i<=k;i++)
		if(b[i]){//若没选过
			b[i]=0;//选过
			printf("%d",i);//先输出
			for(int j=1;j<=k;j++)
				if(b[j]){//若没选过
					bz=true;//判断树i,j是否同构
					for(int l=1;l<=n;l++)
						if(s[i][l]!=s[j][l]){
							bz=false;
							break;
						}
					if(bz)//相同
						b[j]=0,printf("=%d",j);//输出
				}
			printf("\n");//换行
		}
}
发布了16 篇原创文章 · 获赞 2 · 访问量 1532

猜你喜欢

转载自blog.csdn.net/jay_zai/article/details/103980219