Skip to content

迭代回溯最大团

约 576 字大约 2 分钟

2024-11-03

思路

解空间: 子集树

约束条件: 当前节点是否能够和所有的已选节点可达

对于每一个节点,左子树表示选择该节点,右子树表示不选择;使用变量 currNum 记录已经选取的节点数

采用 根-左-右 的遍历方法,即优先考虑选取该节点,后考虑不选取该节点两种情况

  • 若到达底部时,若当先选择的节点数大于最大节点数,则替换

  • 若未到达底部,因为该结点无法继续选取,先判断取右子树:假如选取剩余所有节点是否能超过最大节点数

    • 若不能则回溯至上一节点,并再次进行判断**(剪枝)**

    • 若能,意味着该节点的右子树仍有可尝试的价值,则取该节点的右子树,到达下一层后从选取左子树开始循环

实现代码

public static void main(String[] args) {
    int max = Integer.MAX_VALUE;
    int[][] graph = {{},
            {max, 1, 1, 0, 1, 1},
            {max, 1, 1, 1, 0, 1},
            {max, 0, 1, 1, 0, 1},
            {max, 1, 0, 0, 1, 1},
            {max, 1, 2, 3, 0, 1}};
    int n = 5;

    maxClique(graph, n);
}

public static void maxClique(int[][] graph, int n) {
    boolean[] isInclude = new boolean[n + 1];
    boolean[] maxC = new boolean[n + 1];
    int maxNum = 0; // 最大的节点数
    int currNum = 0; // 当前已取的节点数

    int i = 1;
    while (true) {
        // 左表示取该点,右表示不取
        // 未到达底部, 且当前节点可取
        // 进入左子树
        while (i <= n && isIncludeAble(isInclude, graph, i)) {
            isInclude[i] = true;
            currNum++;
            i++;
        }
        // 到达底部
        if (i > n) {
            if (currNum > maxNum) {
                maxNum = currNum;
                for (int j = 1; j <= n; ++j) {
                    maxC[j] = isInclude[j];
                }
            }
        } else {
            // 未到达底部
            i++;
        }
        // 若剩余的节点加上已选节点不能大于最大节点数,回溯
        while (currNum + n - i <= maxNum) {
            i--;
            // 如果当前位置为右子数
            while (i > 0 && !isInclude[i]) {
                i--;
            }
            // 当从根节点的右子树回溯完成
            if (i == 0) {
                System.out.println("最大团为");
                for (int j = 1; j <= n; ++j) {
                    if (maxC[j]) {
                        System.out.print(j + " ");
                    }
                }
                return;
            }
            // 从左子树回溯完成后进入右子树
            isInclude[i] = false;
            currNum--;
            i++;
        }
    }
}

public static boolean isIncludeAble(boolean[] isInclude, int[][] graph, int i) {
    for (int j = 1; j < isInclude.length; ++j) {
        // 若对于已取的节点与将要取的节点 i 不可达
        if (isInclude[j] && graph[j][i] != 1) {
            return false;
        }
    }
    return true;
}