Atcoder abc187 F Close Group
题目
给出一张n个点,m条边的无向图,问删除任意数量的边后,留下来的最少数量的团的个数(\(n \le 18\) )
题解
核心:枚举状态+动态规划
第一次枚举状态,对状态进行预处理,判断状态里所有的1是否能够形成一个团
第二次枚举状态S,再对每个状态枚举子状态T,假如T是一个团,那么 就可以进行动态递推
\[dp[S]=min(dp[S],dp[S\wedge T]+1)
\]
复杂度分析
状态S中1的个数有x个,那S的子集就有 \(2^x\) 个,同时这样的S有 \(C_{18}^x\) 个
所以最后的循环次数为
\[C_{18}^0*2^0+C_{18}^1*2^1+\cdots+C_{18}^x*2^x+\cdots=(1+2)^{18}
\]
最坏的情况是\(3^{18}\) ,3e8的复杂度
代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<map>
#include<cstring>
using namespace std;
int a[100][100];
const int N=1e6;
int L[N],dp[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
a[x][y]=1;
a[y][x]=1;
}
int R=1<<n;
for(int S=0;S<R;S++){
int flag=1;
for(int i=1;i<=n;i++){
int x1=i-1;
if(!((S>>x1)&1)) continue;
for(int j=i+1;j<=n;j++){
int x2=j-1;
if(!((S>>x2)&1)) continue;
if(a[i][j]==0){
flag=0;
break;
}
}
if(flag==0) break;
}
L[S]=flag;
}
for(int S=1;S<R;S++){
dp[S]=n+1;
for(int T=S;T;T=(T-1)&S){
if(L[T]) dp[S]=min(dp[S],dp[T^S]+1);
}
}
printf("%d\n",dp[R-1]);
}