最大团

考试遇到的例题。

多手推几组样例,再用一下均值不等式,(实际很难)可以发现实际上就是求最大联通子图即最大团。

团:每个顶点都两两相连。

极大团:没有被包含在其他团中的团。

最大团:定点数最多的极大团。

然后我们就需要学习最大团的算法。

Bron–Kerbosch

设P表示有可能加入当前在找的极大团里的点,R表示当前正在找的极大团里的点,X表示已经找到的极大团里的点(用来判重,和时间复杂度无关)

每次搜索\(R\cup{u},P\cap nxt(u),X\cap nxt(u)\)\(nxt(u)\)表示和 \(u\) 相邻的点。其中能保证正确性的是P集合一定是在最大团中的相邻点集合的交,那么也就能保证加入的点一定和前面的点构成完全子图,P为零时是极大团,且X为0时代表还没有算过这个方案。

优化方面,可以加一个关键点优化,就是在P集合里,如果选择加入了某个点u,那么遍历到与之相邻的v时不需要加入,因为在u的递归层会遍历到。

据说时间复杂度是\(O(3^{n/3})\),但又据说跑得很快。

例题
#include<bits/stdc++.h>
using namespace std;
int n,m,t,a[45][45],vis[45][45],r[45][45],p[45][45],num,mx;
void bk(int d,int R,int P,int X){
	if(!P&&!X){
		mx=max(mx,R);
		return ;
	}
	int u=p[d][1];
	for(int i=1;i<=P;i++){
		int v=p[d][i];
		if(a[u][v]) continue;//实际上优化就这个 
		for(int j=1;j<=R;j++) vis[d+1][j]=vis[d][j];
		vis[d+1][R+1]=v;
		int np=0,nx=0;
		for(int j=1;j<=P;j++){
			if(a[v][p[d][j]]) p[d+1][++np]=p[d][j];
		}
		for(int j=1;j<=X;j++){
			if(a[v][vis[d][j]]) vis[d+1][++nx]=vis[d][j];
		}
		bk(d+1,R+1,np,nx);
		p[d][i]=0,vis[d][++X]=v;
	}
}
int main(){
//	freopen("nanami.in","r",stdin);
//	freopen("nanami.out","w",stdout);
	scanf("%d%d%d",&n,&m,&t);
	for(int i=1,u,v;i<=m;i++){
		scanf("%d%d",&u,&v);
		a[u][v]=a[v][u]=1;
	}
	for(int i=1;i<=n;i++) p[1][++num]=i;
	bk(1,0,num,0);
	double ans=0.5*(1.0-1.0/mx)*t*t;
	printf("%.6lf",ans);
	return 0;
} 
posted @ 2024-10-10 22:04  YYYmoon  阅读(12)  评论(0编辑  收藏  举报