定义:
团:从一个无向图中找出一个点集,使得此集合中任意两个点都有一条直接相连的边。
极大团:不是任何其他团的子图的团
最大团:极大团中点数最多的团
定理:
一个图的最大独立集等于其补图的最大团
Born_Kerbosch算法
从后往前枚举每一个点为第一个点,选择的其他点为大于这个点的情况。用vec[]记录当前选择的点的集合,判断一个点是否可以选择,只需要枚举已选择的点是否都与之直接相连。
优化: 用num[]记录每个点往后的最优情况(最大团大小),如果最优情况加上当前点数量都不能比已有答案ans更优,直接return。
#include<bits/stdc++.h>
using namespace std;
const int maxn=53;
int mp[maxn][maxn];
int ans_vec[maxn],vec[maxn],num[maxn];//num最后是递减的
//答案团,当前团,每个位置开始遍历得到的最大团
int n,ans;
int dfs(int p,int siz){
for(int i=p+1;i<=n;i++){//从前往后遍历每个点
if(num[i]+siz<=ans)return 0;//最优性剪枝
if(mp[p][i]){
int can=1;
for(int j=1;j<=siz;j++){
if(!mp[vec[j]][i]){
can=0;break;
}
}
if(can){//如果和已有集合均有边,则加入集合
vec[siz+1]=i;
if(dfs(i,siz+1))return 1;//更优的答案已经记录,无需更新
}
}
}
if(siz>ans){
ans=siz;
for(int i=1;i<=ans;i++)ans_vec[i]=vec[i];
return 1;
}
return 0;
}
int deal(){
ans=-1;
for(int i=n;i>=1;i--){
vec[1]=i;//开始只有i
dfs(i,1);
num[i]=ans;
}
return ans;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&mp[i][j]);
}
}
int res=deal();
printf("%d
",res);
}
更快更短的写法:
#include <bits/stdc++.h>
using namespace std;
const int N = 45;
int n, k, g[N][N], ans[N], answer;
void dfs(int sum, int v[], int len) {
answer = max(answer, sum);
int v1[N], len1;
for(int i = 1; i <= len; ++i) {
if(sum + len - i + 1 <= answer) break;
if(sum + ans[v[i]] <= answer) break;//最优性剪枝
len1 = 0;
for(int j = i + 1; j <= len; ++j) if(g[v[i]][v[j]]) v1[++len1] = v[j];
dfs(sum + 1, v1, len1);
}
}
inline void MaxClique() {
int valid[N], len;
answer = ans[n] = 1;
for(int i = n - 1; i >= 1; --i) {
len = 0;
for(int j = i + 1; j <= n; ++j) if(g[i][j]) valid[++len] = j;
dfs(1, valid, len);
ans[i] = answer;
}
}
int main() {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
scanf("%d", &g[i][j]);
MaxClique();
printf("%d
",answer);
return 0;
}