给定无向图G=(V,E),其中V是非空集合,称为顶点集;E是V中元素构成的无序二元组的集合,称为边集,无向图中的边均是顶点的无序对,无序对常用圆括号“( )”表示。
如果U V,且对任意两个顶点u,v∈U有(u,v)∈E,则称U是G的完全子图。G的完全子图U是G的团。G的最大团是指G的最大完全子图。
如果U∈V且对任意u,v∈U有(u,v)不属于E,则称U是G的空子图。G的空子图U是G的独立集当且仅当U不包含在G的更大的空子图中。G的最大独立集是G中所含顶点数最多的独立集。
对于任一无向图G=(V,E),其补图G'=(V',E')定义为:V'=V,且(u,v)∈E'当且仅当(u,v)∉E。
如果U是G的完全子图,则它也是G'的空子图,反之亦然。因此,G的团与G'的独立集之间存在一一对应的关系。特殊地,U是G的最大团当且仅当U是G'的最大独立集。
通俗点讲就是在一个无向图中找出一个点数最多的完全图。
最大独立集 = 补图的最大团
最大团 = 补图的最大独立集
链接:hdu - 1530
最大团模板:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1005;
/*
最大团 = 补图G的最大独立集数
———>最大独立集数 = 补图G'的最大团
*/
//最大团模板
bool mp[maxn][maxn];//mp为图的邻接表(从1开始)
int ans, cnt[maxn], group[maxn], n, m, vis[maxn];//ans表示最大团,cnt[maxn]表示当前最大团的节点数,group[maxn]用以寻找一个最大团集合
bool dfs(int u, int pos) {
//u为当从前顶点开始深搜,pos为深搜深度(即当前深搜树所在第几层的位置)
for(int i = u + 1; i <= n; i++) {
int j;
//按递增顺序枚举顶点
if(cnt[i] + pos <= ans) //剪枝
return 0;
if(mp[u][i]) {
// 与目前团中元素比较,取 Non-N(i)
for(j = 0; j < pos; j++)
if(!mp[i][vis[j]])
break;
if(j == pos) {
// 若为空,则皆与 i 相邻,则此时将i加入到 最大团中
vis[pos] = i;//深搜层次也就是最大团的顶点数目,vis[pos] = i表示当前第pos小的最大团元素为i(因为是按增顺序枚举顶点 )
if(dfs(i, pos + 1))
return 1;
}
}
}
if(pos > ans) {
for(int i = 0; i < pos; i++)
group[i] = vis[i]; // 更新最大团元素
ans = pos;
return 1;
}
return 0;
}
void maxclique() {//求最大团
ans = -1;
for(int i = n; i > 0; i--) {
vis[0] = i;
dfs(i, 1);
cnt[i] = ans;
}
}
int main()
{
while(~scanf("%d", &n)) {
if(n == 0) break;
memset(mp, 0, sizeof(mp));
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
scanf("%d", &mp[i][j]);
maxclique();//求最大团
if(ans < 0)
ans = 0;//ans表示最大团
printf("%d\n", ans);
/*for(int i = 0; i < ans; i++)
printf( i == 0 ? "%d" : " %d", group[i]); //group[maxn]用以寻找一个最大团集合
if( ans > 0 ) puts("");*/
}
}