树的同构

本文介绍了三种判断树同构的方法:暴力枚举法、哈希法及尝试通过节点属性排序进行比对的方法。每种方法均提供了详细的实现思路与代码示例。

当树为无根树时,可以枚举每一的树的节点作为根,然后判相应的有根树的同构了.

这样问题就是怎么判断两个有根树是否同构.

解法1 : 暴力枚举,就是对于两棵树,递归的比较两个数的子树,如果存在两个树的子树之间的某一个一一配对,使所有相应的子树都同构,那么这两棵树同构.

附代码:[POJ_1635]

ExpandedBlockStart.gif View Code

      
1 // 树的最小表示,以及判定有根树是否同构
2   // 其实就是构造出来树,然后再递归的比较一下,或许是数据弱
3 #include < iostream >
4 #include < cstring >
5 #include < algorithm >
6 #include < climits >
7 #include < cstdio >
8 #include < cmath >
9
10 using namespace std;
11
12 int tree1[ 1510 ][ 1510 ];
13 int tree2[ 1510 ][ 1510 ];
14 int len1[ 1510 ];
15 int len2[ 1510 ];
16 char str1[ 3100 ],str2[ 3100 ];
17 int stk[ 1510 ];
18 int cnt1[ 1510 ], cnt2[ 1510 ];
19 bool vis[ 1510 ];
20
21 inline bool cmp1( int a, int b)
22 {
23 if (cnt1[a] != cnt1[b])
24 return cnt1[a] < cnt1[b];
25
26 return len1[a] < len1[b];
27 }
28
29 inline bool cmp2( int a, int b)
30 {
31 if (cnt2[a] != cnt2[b])
32 return cnt2[a] < cnt2[b];
33 return len2[a] < len2[b];
34 }
35
36 void DFS1( int x)
37 {
38 cnt1[x] = 1 ;
39 for ( int i = 0 ; i < len1[x]; i ++ )
40 {
41 DFS1(tree1[x][i]);
42 cnt1[x] += cnt1[tree1[x][i]];
43 }
44 }
45 void DFS2( int x)
46 {
47 cnt2[x] = 1 ;
48 for ( int i = 0 ; i < len2[x]; i ++ )
49 {
50 DFS2(tree2[x][i]);
51 cnt2[x] += cnt2[tree2[x][i]];
52 }
53 }
54
55 void build()
56 {
57 int l = strlen(str1);
58 memset(len1, 0 , sizeof (len1));
59 memset(len2, 0 , sizeof (len2));
60 int cur = 0 ;
61 int cnt = 0 ;
62 int top = 0 ;
63 for ( int i = 0 ; i < l; i ++ )
64 {
65 switch (str1[i])
66 {
67 case ' 0 ' :
68 {
69 stk[ ++ top] = cur;
70 tree1[cur][len1[cur] ++ ] = ++ cnt;
71 cur = cnt;
72 break ;
73 }
74 case ' 1 ' :
75 {
76 cur = stk[top -- ];
77 break ;
78 }
79 }
80 }
81 cur = 0 ;
82 cnt = 0 ;
83 top = 0 ;
84 for ( int i = 0 ; i < l; i ++ )
85 {
86 switch (str2[i])
87 {
88 case ' 0 ' :
89 {
90 stk[ ++ top] = cur;
91 tree2[cur][len2[cur] ++ ] = ++ cnt;
92 cur = cnt;
93 break ;
94 }
95 case ' 1 ' :
96 {
97 cur = stk[top -- ];
98 break ;
99 }
100 }
101 }
102 memset(cnt1, 0 , sizeof (cnt1));
103 memset(cnt2, 0 , sizeof (cnt2));
104 DFS1( 0 );
105 DFS2( 0 );
106 }
107
108 bool CMP( int a, int b)
109 {
110 if (len1[a] != len2[b] || cnt1[a] != cnt2[b])
111 {
112 return false ;
113 }
114 int x, y;
115
116 for ( int i = 0 ; i < len1[a]; i ++ )
117 {
118 bool bingo = false ;
119 x = tree1[a][i];
120 for ( int j = 0 ; j < len2[b]; j ++ )
121 {
122 y = tree2[b][j];
123 if (vis[y])
124 continue ;
125 if (len1[x] != len2[y] || cnt1[x] != cnt2[y])
126 continue ;
127 if (CMP(x, y))
128 {
129 vis[y] = true ;
130 bingo = true ;
131 break ;
132 }
133 }
134 if ( ! bingo)
135 {
136 for ( int j = 0 ; j < len2[b]; j ++ )
137 {
138 vis[tree2[b][j]] = false ;
139 }
140 return false ;
141 }
142 }
143 for ( int j = 0 ; j < len2[b]; j ++ )
144 {
145 vis[tree2[b][j]] = false ;
146 }
147 return true ;
148 }
149
150 int main()
151 {
152 freopen( " input.txt " , " r " , stdin);
153
154 int T;
155 gets(str1);
156 sscanf(str1, " %d " , & T);
157 while (T -- )
158 {
159 gets(str1);
160 gets(str2);
161 if (strlen(str1) != strlen(str2))
162 {
163 puts( " different " );
164 continue ;
165 }
166
167 build();
168 memset(vis, false , sizeof (vis));
169 if (CMP( 0 , 0 ))
170 {
171 puts( " same " );
172 }
173 else
174 {
175 puts( " different " );
176 }
177 }
178 return 0 ;
179 }

 

解法2:hash,算法描述见07年国家集训队论文-杨弋《Hash在信息学竞赛中的一类应用》

附代码:[POJ_1635]

ExpandedBlockStart.gif View Code

      
1 #include < iostream >
2 #include < fstream >
3 #include < algorithm >
4 #include < cstring >
5 #include < climits >
6 #include < cmath >
7
8 using namespace std;
9
10 int h[ 11000 ];
11 char str1[ 3100 ], str2[ 3100 ];
12 char * p;
13
14 int Hash( int j)
15 {
16 int sum = h[j + 5000 ]; // 这里的j是记录的节点度
17 while ( * p && * p ++ == ' 0 ' ) // 这个巧妙的循环,把子节点的hash值都加给了父节点,作为父节点的hash值
18 {
19 sum = (sum + h[j] * Hash(j + 1 )) % 19001 ;
20 }
21 return (sum * sum) % 19001 ;
22 }
23
24 inline void init()
25 {
26 for ( int i = 0 ; i < 10000 ; i ++ )
27 h[i] = (rand() % 19901 );
28 }
29
30 int main()
31 {
32 // freopen("input.txt", "r", stdin);
33 int T;
34 scanf( " %d " , & T);
35 init();
36 while (T -- )
37 {
38 scanf( " %s%s " , str1, str2);
39 p = str1;
40 int a = Hash( 1 );
41 p = str2;
42 int b = Hash( 1 );
43
44 if (a == b)
45 {
46 puts( " same " );
47 }
48 else
49 {
50 puts( " different " );
51 }
52 }
53 return 0 ;
54 }

 

解法3:这种解法是从网上看到的,思路:一棵树统计每个节点的子节点个数,每个节点的深度,最后给每个节点排序,即可当成树的最小表示。最小表示相同的树同构。

但是这个解法是有错误的,已经被GG牛给cha掉了,

附上被cha的代码:[POJ_1635]

ExpandedBlockStart.gif View Code

      
1 #include < iostream >
2 using namespace std;
3 const int maxn = 3005 ;
4 struct node
5 {
6 int depth,son;
7 }tree[maxn],tree2[maxn];
8 int CMP( const void * a, const void * b)
9 {
10 node * p = (node * )a;
11 node * q = (node * )b;
12 if (p -> depth == q -> depth) return p -> son - q -> son;
13 else return p -> depth - q -> depth;
14 }
15 int min_pre( char str[],node result[]) // 无回路的图,选定根节点,即可确定一棵树。一棵树统计每个节点的子节点个数,每个节点的深度,最后排序,即可当成树的最小表示。最小表示相同的树同构。
16 {
17 int node_num = 1 ,now = 0 ;
18 int father[maxn];
19 father[ 0 ] = result[ 0 ].son = result[ 0 ].depth = 0 ;
20 for ( int i = 0 ;str[i];i ++ )
21 {
22 if (str[i] == ' 0 ' )
23 {
24 father[node_num] = now;
25 result[node_num].depth = result[father[node_num]].depth + 1 ;
26 result[node_num].son = 0 ;
27 now = node_num ++ ;
28 }
29 else
30 {
31 result[father[now]].son += result[now].son + 1 ;
32 now = father[now];
33 }
34 }
35 qsort(result,node_num, sizeof (result[ 0 ]),CMP);
36 return node_num;
37 }
38 int main()
39 {
40 char str[maxn];
41 int T;
42 scanf( " %d " , & T);
43 for ( int t = 0 ;t < T;t ++ )
44 {
45 scanf( " %s " ,str);
46 int num = min_pre(str,tree);
47 scanf( " %s " ,str);
48 int num2 = min_pre(str,tree2);
49 if (num != num2)
50 {
51 printf( " different\n " );
52 continue ;
53 }
54 int i;
55 for (i = 0 ;i < num;i ++ )
56 {
57 if (tree[i].depth != tree2[i].depth || tree[i].son != tree2[i].son)
58 {
59 printf( " different\n " );
60 break ;
61 }
62 }
63 if (i >= num) printf( " same\n " );
64 }
65 return 0 ;
66 }

 

cha的数据:

1

00101011000111

00100111001011

然后我修改了一下节点深度的设置,又被cha掉,至此死心.

反思是,这个算法是变相的hash,但是准确程度远不及算法2.

Thx to GG牛.

posted on 2011-06-01 00:38  Jackiesteed 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/jackiesteed/articles/2065307.html

标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广泛使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模型构建、验证与优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模型 逻辑回归是一种常用于分类任务的线性模型,特别适用于二类问题。在信用评估中,该模型可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模型 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约与非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理与转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模型的适应性。 六、模型构建与验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模型性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模型的召回率与特异性。 七、集成学习方法 为提升模型预测能力,可采用集成策略,如结合多个模型的预测结果。这有助于降低单一模型的偏差与方差,增强整体预测的稳定性与准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理与模型优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模型以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值