最大团问题(Maximum Clique Problem,MCP)
通俗点讲就是在一个无向图中找出一个点数最多的完全子图
问题描述
找出该图的最大团
重温相关的图论概念
-
完全子图
给定无向图G=(V,E),其中V是顶点集,E是边集。G’=(V’,E’)如果顶点集V’∈V,E’∈E,且G’种任意两个点有边相连,则称G’是G的完全子图
(用更简洁的语言来说就是,某个图的所有顶点是另一个图的顶点子集,该图的边是另一个图的边集,并且任意两个顶点之间都有边) -
团
即极大完全子图
当且仅当G不包含在G‘更大的子图中 -
最大团
在G’的所有团中包含顶点数最多的团
定义解空间
由题目可知,图中所有顶点仅含有两个对立属性,即属于最大团和不属于最大团,因此其解空间是由顶点的是否进入最大团组成的
确定解空间结构
在定义解空间时即可看出和01背包问题、装载问题相像的解空间,是一棵完全二叉树,所以解空间结构即是子集树
剪枝函数
进入左子树时判断顶点是否与已入团顶点有边
进入右子树判断判断最多能够有多少顶点,是否能够产生更优解
代码实现
package MaximumCliqueProblem;
import java.util.*;
public class MCP {
static int[] x; // 当前解
static int n; // 图G的顶点数
static int cn; // 当前顶点数
static int bestn; // 当前最大顶点数
static int[] bestx; // 当前最优解
static boolean[][] matrix; // 图G的邻接矩阵
/**
* @param m
* 是邻接矩阵
* @param v
* out the best solution 最优解
* @return the best result 最优值
*/
public static int maxClique(boolean[][] m, int[] v) {
matrix = m; //邻接矩阵必然是正方形
n = matrix.length;
x = new int[n]; //当前解
cn = 0;
bestn = 0;
bestx = v; //刚开始V是什么都没有
backtrack(0);
return bestn;
}
private static void backtrack(int i) {
//只有到达5才到这里。
if (i == n) {
// 到达叶结点
for (int j = 0; j < n; j++) {
bestx[j] = x[j];
}
bestn = cn;
}
// 检查顶点 i 与当前团的连接
boolean connected = true;
for (int j = 0; j < i; j++) {
if (x[j] == 1 && !matrix[i][j]) {
// i 和 j 不相连
connected = false;
break;
}
}
if (connected) {
// 进入左子树
x[i] = 1;
cn++; //当前进了几步
trackback(i + 1);
cn--;
}
if (cn + n - i > bestn) { //剪枝函数,如果比最优解还大,继续才有意义 n是可能的最优解有n个,加上cn表示已经找到解的个数,i表示已经走了的步数,才可能有最优解
// 进入右子树
x[i] = 0;
trackback(i + 1);
}
}
private static void test() {
boolean[][] matrix = { { false, true, false, true, true },
{ true, false, true, false, true },
{ false, true, false, false, true },
{ true, false, false, false, true },
{ true, true, true, true, false } };
int[] v = new int[matrix.length];
int bestn = maxClique(matrix, v); //返回值计算最大团个数
System.out.println("最优解是: " + bestn);
System.out.println(Arrays.toString(bestx));
}
public static void main(String[] args) {
test();
}
}