从浏览器到事务码,读懂 SAP S/4HANA 的统一系统访问体系
2026/7/24 23:59:12
pythonclass UnionFind: def __init__(self, n): # 每个动物有3个域,所以总大小为3*n self.parent = list(range(3 * n)) self.rank = [0] * (3 * n) def find(self, x): # 路径压缩 if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): # 按秩合并 x_root = self.find(x) y_root = self.find(y) if x_root == y_root: return if self.rank[x_root] < self.rank[y_root]: self.parent[x_root] = y_root elif self.rank[x_root] > self.rank[y_root]: self.parent[y_root] = x_root else: self.parent[y_root] = x_root self.rank[x_root] += 1 def same(self, x, y): # 判断两个域是否在同一集合 return self.find(x) == self.find(y)def solve_food_chain(n, statements): """ n: 动物数量(编号1~n) statements: 陈述列表,每个元素为(d, x, y) 其中d=1表示同类,d=2表示x吃y 返回假话数量 """ uf = UnionFind(n + 1) # 为了方便,从1开始编号 false_count = 0 for d, x, y in statements: # 检查明显假话:x或y超出范围 if x > n or y > n: false_count += 1 continue # 定义三个域的索引 # 域0: 同类域(自身) # 域1: 吃域(x吃谁) # 域2: 被吃域(谁吃x) def get_domain(animal, domain_type): return animal * 3 + domain_type if d == 1: # 同类关系 # 如果x和y已经是吃或被吃关系,则为假话 if (uf.same(get_domain(x, 0), get_domain(y, 1)) or uf.same(get_domain(x, 0), get_domain(y, 2))): false_count += 1 continue # 合并三个域 uf.union(get_domain(x, 0), get_domain(y, 0)) uf.union(get_domain(x, 1), get_domain(y, 1)) uf.union(get_domain(x, 2), get_domain(y, 2)) else: # 吃关系 (d==2) # 如果x和y已经是同类或反向吃关系,则为假话 if (uf.same(get_domain(x, 0), get_domain(y, 0)) or uf.same(get_domain(x, 0), get_domain(y, 1))): false_count += 1 continue # 合并x的同类域与y的被吃域 uf.union(get_domain(x, 0), get_domain(y, 2)) # 合并x的吃域与y的同类域 uf.union(get_domain(x, 1), get_domain(y, 0)) # 合并x的被吃域与y的吃域 uf.union(get_domain(x, 2), get_domain(y, 1)) return false_count# 测试用例n = 100statements = [ (1, 1, 2), # 1和2是同类 (2, 2, 3), # 2吃3 (2, 1, 3), # 1吃3 → 根据前两条,1和2同类,2吃3,所以1也应该吃3,此句为真 (2, 3, 1), # 3吃1 → 根据环,3吃1也是对的(因为A吃B,B吃C,C吃A) (1, 1, 3), # 1和3是同类 → 但1吃3,不能是同类,所以假话]print("假话数量:", solve_food_chain(n, statements)) # 输出:1运行结果会输出1,说明最后一条陈述是假话。## 五、如何判断扩展域的数量?通过上面的例子,你可能已经发现:扩展域的数量等于元素可能处于的状态种类数。更具体地说:- 如果有两种对立关系(朋友/敌人),需要2个域- 如果有三种循环关系(吃/被吃/同类),需要3个域- 如果有四种或更多关系,也需要相应数量的域但要注意:扩展域的数量不能随意增加。每个域必须代表互斥的状态。比如在食物链中,一个动物不能同时是“吃”和“被吃”状态,它们是互斥的。## 六、更复杂的例子:带权关系扩展域并查集也可以处理带权的关系。比如我们要判断一个社交网络中是否有矛盾:如果A和B是朋友(权值+1),B和C是敌人(权值-1),那么A和C的关系应该是敌人还是朋友?我们可以用3个域来表示状态:- 域0:朋友的朋友- 域1:朋友的敌人- 域2:敌人的敌人实际上,这相当于用模3的余数来表示关系。下面是一个带权逻辑的扩展域实现:pythonclass ExtendedUnionFind: def __init__(self, n, domains=3): # domains表示每个元素有几个域 self.parent = list(range(n * domains)) self.rank = [0] * (n * domains) self.domains = domains def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): x_root = self.find(x) y_root = self.find(y) if x_root == y_root: return if self.rank[x_root] < self.rank[y_root]: self.parent[x_root] = y_root elif self.rank[x_root] > self.rank[y_root]: self.parent[y_root] = x_root else: self.parent[y_root] = x_root self.rank[x_root] += 1 def add_relation(self, a, b, relation_type): """ 添加a和b之间的关系 relation_type: 0表示同类,1表示a吃b(或a是b的敌人) 这里以食物链为例,但你可以自定义 """ # 假设有3个域,我们使用模运算来映射关系 # 域索引: a的域0与b的域(relation_type)合并 for i in range(self.domains): self.union(a * self.domains + i, b * self.domains + (i + relation_type) % self.domains) def is_consistent(self, a, b, relation_type): """ 检查a和b的关系是否与当前已知关系一致 返回True表示一致,False表示矛盾 """ # 检查是否存在矛盾:a的域0与b的域(relation_type)是否在同一集合 return (self.find(a * self.domains) == self.find(b * self.domains + relation_type))# 测试带权关系uf = ExtendedUnionFind(4, domains=3)# 假设关系:1和2是同类(0),2吃3(1),那么1应该吃3uf.add_relation(1, 2, 0) # 同类uf.add_relation(2, 3, 1) # 2吃3print("1和3是吃关系是否一致:", uf.is_consistent(1, 3, 1)) # Trueprint("1和3是同类是否一致:", uf.is_consistent(1, 3, 0)) # False这个例子展示了如何用扩展域来检查隐含关系的正确性。## 七、总结扩展域并查集是普通并查集的一个强大扩展,它通过为每个元素创建多个互斥的状态域,来应对复杂的关系推理。它的核心优势在于:1.能处理对立和循环关系:比如敌人的敌人是朋友、食物链中的循环捕食关系2.代码实现简单:只需要在普通并查集的基础上增加域的数量,合并规则根据关系类型定义3.应用广泛:从判断逻辑矛盾到解决约束满足问题,都有它的身影使用扩展域并查集的关键在于:-确定元素有多少种互斥状态,从而决定域的数量-理清合并规则:每种关系对应哪些域的合并-注意矛盾检测:在合并前先检查是否与已有关系冲突掌握扩展域并查集,不仅能提高解题能力,更能拓展你对“关系”和“状态”的建模思维。下次遇到需要推理隐含关系的问题时,不妨试试用它来求解吧!