最大团
考试遇到的例题。
多手推几组样例,再用一下均值不等式,(实际很难)可以发现实际上就是求最大联通子图即最大团。
团:每个顶点都两两相连。
极大团:没有被包含在其他团中的团。
最大团:定点数最多的极大团。
然后我们就需要学习最大团的算法。
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;
}