最大团相关问题 & 题解:P12371 最大团/最大独立集

· · 题解

Solution

显然最大独立集可以转为补图的最大团,所以只需要搞定最大团怎么找。

复杂度 O(n 2^{\frac{n}{2}}) 的最大团算法

考虑枚举团中编号最小的点 i,考虑剩下的 n-i 个点。

将其分为两部分 AB。在 AB 上分别做 O(2^{|S|}) 的指数级算法,得到内部所有可能的团。

这是一个高维前缀和问题,所以可以做到 $O((n-i) 2^{\frac{n-i}{2}})$。求和后还是 $O(n2^{\frac{n}{2}})$。 ## 复杂度 $O(2^{\frac{n}{2}})$ 的最大团算法 其实复杂度瓶颈就在算高维前缀和上。 考虑高维前缀和的具体含义:对于集合 $S$,算出所有包含在 $S$ 中的团的信息,记为 $f_S$。 令 $u$ 为 $S$ 中任何一个元素,$T_u$ 为 $u$ 在 $S$ 中的邻域。 如果 $u$ 不在团中,由 $f_{S / \{u\}}$ 转移;如果 $u$ 在团中,由 $f_{T_u} + 1$ 转移。所以可以做到 $O(2^{\frac{n-i}{2}})$ 单次。求和后为 $O(2^{\frac{n}{2}})$。 ## 复杂度 $O(\sqrt m 2^{\frac{\sqrt{2m}}{2}})$ 的最大团算法 其实没啥本质不同。类似三元环计数的手法,将所有点按照度数排序。这样可以控制每个点比他大且有连边的点个数在 $\sqrt {2m}$ 之内。跑和上面一样的方法即可。 根据琴生不等式(其实也不是琴生不等式,就是最基础的凸函数求最值),最大值为 $O(\sqrt m 2^{\frac{\sqrt{2m}}{2}})$。这种方法在稀疏图上非常优秀。 ## 最大独立集的一个搜索做法 哎我去年学过这个东西,怎么给忘了呢。 如果所有点的度数都 $\le 2$,那么只有环和链,最大独立集是容易处理的。 考虑枚举度数最大的点是否在独立集里面,如果在就把其邻域内所有点都删掉。因此复杂度满足递推式 $T(n) = T(n-1) + T(n-4)$,得到 [$T(n) = O(1.3803^n)$](https://www.wolframalpha.com/input?i2d=true&i=Power%5Bx%2C4%5D%3DPower%5Bx%2C3%5D%2B1&lang=zh)。 哎我的 $O(2^{\frac{n}{2}})$ 怎么在代码难度和时间复杂度上都被偏序了。难过。 放一个 $O(2^{\frac{n}{2}})$ 的代码,略微卡常: ```cpp #include<bits/stdc++.h> #define ll long long #define ffor(i,a,b) for(int i=(a);i<=(b);i++) #define roff(i,a,b) for(int i=(a);i>=(b);i--) using namespace std; const int MAXN=50+5; struct INFO {int mx,cnt;ll ex;}F[(1<<25)+10]; INFO operator +(INFO A,INFO B) {if(A.mx>B.mx) return A;if(B.mx>A.mx) return B;return {A.mx,A.cnt+B.cnt,A.ex};} INFO operator +(INFO A,ll S) {return A.mx+=__builtin_popcountll(S),A.ex|=S,A;} int n,m; ll T[MAXN],FF[MAXN]; int NXT2[MAXN],NXT1[MAXN],n1,n2,f1[(1<<25)+10]; ll idx[(1<<25)+10]; bool ok[(1<<25)+10]; INFO calc(int u) { vector<int> nxt; ffor(v,u+1,n) if(T[u]&(1ll<<v-1)) nxt.push_back(v); n1=n2=0; for(auto id:nxt) { if(n2<=n1) NXT2[++n2]=id; else NXT1[++n1]=id; } INFO ans={-1,0,0}; ffor(i,1,n) {FF[i]=0;ffor(j,1,n1) if(T[i]&(1ll<<NXT1[j]-1)) FF[i]|=(1<<j-1);} F[0]={0,1,0}; ffor(i,1,(1<<n1)-1) { int lb=(i&-i),id=NXT1[1+(int)log2(lb)]; F[i]=F[i-lb]+(F[FF[id]&i]+(1ll<<id-1)); } ok[0]=1,f1[0]=(1<<n1)-1; ffor(i,0,(1<<n2)-1) { if(i) { ok[i]=0; int lb=(i&-i),id=NXT2[1+(int)log2(lb)]; if(ok[i-lb]&&((idx[i-lb])&T[id])==idx[i-lb]) ok[i]=1,f1[i]=f1[i-lb]&FF[id],idx[i]=idx[i-lb]|(1ll<<id-1); } if(ok[i]) ans=ans+(F[f1[i]]+idx[i]); } return ans+(1ll<<u-1); } INFO solve(void) { INFO ans={-1,0,0}; ffor(i,1,n) ans=ans+calc(i); return ans; } int main() { ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); cin>>n>>m; ffor(i,1,m) { int u,v; cin>>u>>v; T[u]|=(1ll<<v-1),T[v]|=(1ll<<u-1); } auto ans=solve(); cout<<ans.mx<<' '<<ans.cnt<<'\n'; ffor(i,1,n) if(ans.ex&(1ll<<i-1)) cout<<i<<' '; cout<<'\n'; ffor(i,1,n) T[i]=(1ll<<n)-1-(1ll<<i-1)-T[i]; ans=solve(); cout<<ans.mx<<' '<<ans.cnt<<'\n'; ffor(i,1,n) if(ans.ex&(1ll<<i-1)) cout<<i<<' '; return 0; } ```