无向图的树宽是图论中一个非常重要的概念。发明了许多图算法,如果您分解出具有小树宽的图,则该算法运行很快。
树宽通常根据树分解来定义。这是一个图以及该图的树分解,由维基百科提供:
树分解是一棵树,其中每个顶点与原始图的顶点子集相关联,具有以下属性:
- 原始图中的每个顶点都在至少一个子集中。
- 原始图中的每个边都在至少一个子集中拥有两个顶点。
- 分解中所有子集包含给定原始顶点的顶点都已连接。
您可以检查上述分解是否遵循这些规则。树分解的宽度是其最大子集的大小减去一。因此,以上分解为二。图的树宽是该图的任何树分解的最小宽度。
在此挑战中,将为您提供一个连接的,无向的图,并且您必须找到其树宽。
虽然很难找到树分解,但还有其他方法可以计算树宽。Wikipedia页面上有更多信息,但是此处未提及的计算树宽的一种方法(最小消除顺序宽度)是算法中常用的一种计算树宽的方法。有关此事实的论文,请参见此处。
在消除顺序中,一次消除一个图形的所有顶点。消除每个顶点后,将添加边,以将该顶点的所有邻居彼此连接。重复此操作,直到所有顶点消失。消除顺序宽度是此过程中要消除的任何顶点具有的最大邻居数。树形宽度等于消除顺序宽度的所有顺序中的最小值。这是一个使用此事实来计算树宽的示例程序:
import itertools
def elimination_width(graph):
max_neighbors = 0
for i in sorted(set(itertools.chain.from_iterable(graph))):
neighbors = set([a for (a, b) in graph if b == i] + [b for (a, b) in graph if a == i])
max_neighbors = max(len(neighbors), max_neighbors)
graph = [edge for edge in graph if i not in edge] + [(a, b) for a in neighbors for b in neighbors if a < b]
return max_neighbors
def treewidth(graph):
vertices = list(set(itertools.chain.from_iterable(graph)))
min_width = len(vertices)
for permutation in itertools.permutations(vertices):
new_graph = [(permutation[vertices.index(a)], permutation[vertices.index(b)]) for (a, b) in graph]
min_width = min(elimination_width(new_graph), min_width)
return min_width
if __name__ == '__main__':
graph = [('a', 'b'), ('a', 'c'), ('b', 'c'), ('b', 'e'), ('b', 'f'), ('b', 'g'),
('c', 'd'), ('c', 'e'), ('d', 'e'), ('e', 'g'), ('e', 'h'), ('f', 'g'), ('g', 'h')]
print(treewidth(graph))
例子:
[(0, 1), (0, 2), (0, 3), (2, 4), (3, 5)]
1
[(0, 1), (0, 2), (1, 2), (1, 4), (1, 5), (1, 6), (2, 3), (2, 4), (3, 4), (4, 6), (4, 7), (5, 6), (6, 7)]
2
[(0, 1), (0, 3), (1, 2), (1, 4), (2, 5), (3, 4), (3, 6), (4, 5), (4, 7), (5, 8), (6, 7), (7, 8)]
3
[(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
4
您将收到图形作为输入,并且必须返回树宽作为输出。输入格式灵活。您可以将边列表,邻接图或邻接矩阵作为输入。如果您想使用其他输入格式,请在评论中提问。您可以假设输入已连接,并且可以将该假设构建为输入格式,例如通过使用边列表。
编辑:不允许使用计算树宽的内置操作。对于未预先指定此内容,我深表歉意。
最短的代码获胜。
(V,E)
,这将是有效的输入吗?