【回溯】最大团问题Python实现

文章目录

    • @[toc]
      • 问题描述
      • 回溯法
      • 时间复杂性
      • `Python`实现

因上努力

个人主页:丷从心

系列专栏:回溯法

果上随缘


问题描述

  • 给定无向图 G = ( V , E ) G = (V , E) G=(V,E),如果 U ⊆ V U \subseteq V UV,且对任意 u u u v ∈ U v \in U vU ( u , v ) ∈ E (u , v) \in E (u,v)E,则称 U U U G G G的完全子图

  • G G G的完全子图 U U U G G G的一个团当且仅当 U U U不包含在 G G G的更大的完全子图中, G G G的最大团是指 G G G中所含顶点数最多的团

  • 如果 U ⊆ V U \subseteq V UV且对任意 u u u v ∈ U v \in U vU,有 ( u , v ) ∉ E (u , v) \notin E (u,v)/E,则称 U U U G G G的空子图

  • G G G的空子图 U U U G G G的独立集当且仅当 U U U不包含在 G G G的更大的空子图中, G G G的最大独立集是 G G G中所含顶点数最多的独立集

  • 对于任意无向图 G = ( V , E ) G = (V , E) G=(V,E),其补图 G ˉ = ( V ′ , E ′ ) \bar{G} = (V^{'} , E^{'}) Gˉ=(V,E)定义为: V ′ = V V^{'} = V V=V E ′ = {   ( u , v ) ∣ ( u , v ) ∉ E   } E^{'} = \set{(u , v) \mid (u , v) \notin E} E={(u,v)(u,v)/E}

  • 如果 U U U G G G的完全子图,则它是 G ˉ \bar{G} Gˉ的空子图,反之亦然,因此, G G G的团与 G ˉ \bar{G} Gˉ的独立集之间存在一一对应关系,特别地, U U U G G G的最大团,当且仅当 U U U G ˉ \bar{G} Gˉ的最大独立集

  • 无向图 G G G G G G的补图 G ˉ \bar{G} Gˉ如下图所示

【回溯】最大团问题Python实现_第1张图片


回溯法

  • G G G的最大团和最大独立集问题都可以看作图 G G G的顶点集 V V V的子集选取问题,因此,可用子集树表示问题的解空间,解最大团问题的回溯法与解装载问题的回溯法十分相似
  • 设当前扩展结点 Z Z Z位于解空间树的第 i i i层,在进入左子树前,必须确认从顶点 i i i到已选入的顶点集中每个顶点都有边相连,在进入右子树前,必须确认还有足够多的可选择顶点,使得算法有可能在右子树中找到更大的团

时间复杂性

  • 解最大团问题的回溯算法所需的计算时间为 O ( n 2 n ) O(n 2^{n}) O(n2n)

Python实现

def find_maximum_clique(graph):
    n = len(graph)
    vertices = list(range(n))
    max_clique = []

    def is_clique(graph, vertices):
        # 约束函数: 判断给定的顶点集合是否构成一个团(完全子图)
        for i in range(len(vertices)):
            for j in range(i + 1, len(vertices)):
                if not graph[vertices[i]][vertices[j]]:
                    return False

        return True

    def bound(current_clique, neighbors):
        # 限界函数
        return len(current_clique) + len(neighbors)

    def backtrack(graph, vertices, current_clique, max_clique):
        if not vertices:
            if len(current_clique) > len(max_clique):
                max_clique.clear()
                max_clique.extend(current_clique)

            return

        vertex = vertices.pop(0)
        current_clique.append(vertex)

        neighbors = []
        for v in vertices:
            if graph[vertex][v]:
                neighbors.append(v)

        # 选择当前顶点并加入团
        if is_clique(graph, current_clique):
            backtrack(graph, neighbors, current_clique, max_clique)

        # 不选择当前顶点
        if bound(current_clique, neighbors) > len(max_clique):
            backtrack(graph, neighbors, current_clique[:-1], max_clique)

        vertices.insert(0, vertex)
        current_clique.pop()

    backtrack(graph, vertices, [], max_clique)

    return max_clique


graph = [
    [0, 1, 0, 1, 1],
    [1, 0, 1, 0, 1],
    [0, 1, 0, 0, 1],
    [1, 0, 0, 0, 1],
    [1, 1, 1, 1, 0]
]

maximum_clique = find_maximum_clique(graph)

print(f'最大团: {maximum_clique}')
最大团: [0, 1, 4]

你可能感兴趣的:(#,回溯法,回溯法,Python)