Vijos 有根树的同构问题【字符串---最小表示法】

背景

经典题

描述

所谓图的同构是指两个图“相同”。图的同构有着广泛的应用,比如当要对一批图施行某种操作的时候,如果能发现其中有一些图是同构的,就可以在这些同构的图中只保留一个,从而降低工作量。例如,图1所示的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个顶点,你的任务是求出这些树在同构关系下的所有等价类(如果两棵树同构,则它们属于同一个等价类)。

格式

输入格式

输入的第1行包含两个整数k(1<=k<=100)和n(1<=n<=50),表示总共有k棵树,每棵都是n个顶点。接下来k行,每行描述一棵树;每行包含n-1对整数,表示这棵树的n-1条有向边;数字间用空格隔开。顶点的编号为1到n,每对整数xy表示存在一条x指向y的有向边。树的编号和在数据中出现的顺序一致,也就是说输入文件中第2行描述的是T1,第3行描述的是T2,……,第k+1行描述的是Tk。

输出格式

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

8=9 
注意,数字和等号之间不要有空格;行首和行末可以有空格。

样例1

样例输入1

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

Copy

样例输出1

1=3
2

Copy

限制

1s

 1 /*
 2     把每棵树按拓扑关系转换成括号序列
 3     每一种拓扑关系的序列是唯一的
 4     只要dfs找到括号序列
 5     再判断相等关系就好了
 6 */
 7 #include<cstdio>
 8 #include<iostream>
 9 #include<algorithm>
10 #define MAXN  110
11 using namespace std;
12
13 int n,m,special[MAXN];
14
15 struct node {
16     int to;
17     int next;
18 };
19 node e[MAXN*MAXN];
20
21 int head[MAXN],tot;
22
23 string s[MAXN];
24
25 inline void read(int&x) {
26     int f=1;x=0;char c=getchar();
27     while(c>‘9‘||c<‘0‘) {if(c==‘-‘) f=-1;c=getchar();}
28     while(c>=‘0‘&&c<=‘9‘) x=10*x+c-48,c=getchar();
29     x=x*f;
30 }
31
32 inline void add(int x,int y) {
33     e[++tot].to=y;
34     e[tot].next=head[x];
35     head[x]=tot;
36 }
37
38 inline bool cmp(string aa,string bb) {
39     int len=aa.length(),len1=bb.length();
40     if(len==len1) {
41         for(int i=0;i<len;i++)
42           if(aa[i]!=bb[i]) return aa[i]<bb[i];
43     }
44     return len<len1;
45 }
46
47 inline string dfs(int u) {
48     string bs[MAXN];
49     int k=1;
50     for(int i=head[u];i;i=e[i].next,k++)
51       bs[k]=dfs(e[i].to);
52     sort(bs+1,bs+1+k,cmp);
53     string t="(";
54     for(int i=1;i<=k;i++)
55       t+=bs[i];
56     t=t+")";
57     return t;
58 }
59
60 int main() {
61     int x,y;
62     read(m);read(n);
63     for(int p=1;p<=m;p++) {
64         for(int i=1;i<n;i++) {
65             read(x);read(y);
66             add(x,y);
67             special[y]=1;
68         }
69         for(int i=1;i<=n;i++) {
70             if(!special[i]) {
71                 s[p]=dfs(i);
72                 break;
73             }
74         }
75         fill(special+1,special+1+n,0);
76         fill(head+1,head+1+n,0);
77         tot=0;
78     }
79     fill(special+1,special+1+n,0);
80 //    for(int i=1;i<=m;i++) cout<<s[i]<<endl;
81     for(int i=1;i<=m;i++) {
82             if(special[i]) continue;
83             printf("%d",i);
84             special[i]=1;
85             for(int j=i+1;j<=m;j++) {
86                 if(s[i]==s[j])
87                   special[j]=1,printf("=%d",j);
88             }
89             printf("\n");
90     }
91     return 0;
92 } 

代码

时间: 2024-10-15 18:25:22

Vijos 有根树的同构问题【字符串---最小表示法】的相关文章

[coj 1353 Guessing the Number]kmp,字符串最小表示法

题意:给一个字符串,求它的最小子串,使得原串是通过它重复得到的字符串的一个子串. 思路:先求最小长度,最小循环长度可以利用kmp的next数组快速得到,求出长度后然后利用字符串最小表示法求循环节的最小表示即可. #pragma comment(linker, "/STACK:10240000") #include <map> #include <set> #include <cmath> #include <ctime> #include

没有来源的题(似乎是什么POI?) 字符串——最小表示法

[题目描述]? 给你两个长度为 \(n\) 的字符串,问能否通过将某一字符串的一个前缀接到该串的后面使得两个字符串相等.若可以,你还可能被要求输出通过上述操作所能得到的字典序最小的字符串. [输入格式] ? ? 第一行两个整数 \(n,T\). ? 接下来两行,每行一个长度为 \(n\) 的字符串. [输出格式] ? 若可以,输出 \(TAK\),否则输出 \(NIE\).如果 \(T=1\),你还需要在下一行输出字典序最小的字符串. [数据范围] \(n \le 1000000\) 首先,什么

【转载】字符串最小表示法-O(n)算法

原博客链接:http://blog.csdn.net/zy691357966/article/details/39854359 未授权,侵权删. 因为这篇博客写得真好..转载了.. 红色的字是原博主写的,蓝色的字是我加的. ------------------------------------------------------------------------------------------------------------------------------------------

字符串最小表示法模版

1 //O(n) 2 char s[N],ss[N*2]; 3 int n=strlen(s+1); 4 for(int i=1;i<=n;i++) 5 ss[i+n]=s[i]; 6 int i=1,j=2,k; 7 while(i<=n && j<=n){ 8 for(k=0;k<n && s[i+k] == s[j+k]; k++); 9 if(k==n) break; 10 if(s[i+k]>s[j+k]){ 11 i=i+k+1;

hdu 2609 字符串最小表示法 虽然不是很懂 还是先贴上来吧。/,。/

还需要再消化一下这个算法.. 今天没有时间了,, 六级过了 就有大把时间 快活啊!#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<set> using namespace std; int getmin(string s) { int n=s.size(); int i=0,j=1,k=0,t; while(i<n &&a

字符串最小表示法 O(N)

#include<iostream> #include<stdio.h> #include<cstring> #define rep(i,n) for(int i=0;i<n;i++) #define sf scanf #define pf printf using namespace std; int MinimumRepresentation(char *s, int l) { int i = 0, j = 1, k = 0, t; while(i <

2018/2/24 每日一学 字符串最小表示法

非常简单,直接上代码: int minimalRepresentation() { int n = strlen(str); int i = 0,j = 1, k = 0; while(i<n && j<n && k<n) { int t = str[(i+k)%n] - str[(j+k)%n] ; if(t == 0) k++; else { if(t>0) i+=k+1; else j+=k+1; if(i==j) j++; k = 0; }

字符串最小表示法

第一种 int get_minstring(char s[]) { int n =strlen(s); int i = 0, j = 1, k; while (i < n&&j < n) { for (k = 0; k < n&&s[(j + k) % n] == s[(i + k) % n]; k++); if (k == n) break; if (s[(i + k)%n]>s[(j + k)%n]) { i = i + k + 1; if (i

字符串最大最小表示法模板 ( 字典序最大最小 )

模板 int getMin(char *s) { int i = 0, j = 1, l; int len = strlen(s); while(i < len && j < len) { for(l = 0; l < len; l++) if(s[(i + l) % len] != s[(j + l) % len]) break; if(l >= len) break; if(s[(i + l) % len] > s[(j + l) % len]) { if