题目描述 传送门
一看完题就觉得是状压DP。
DP方程不难想:设
d(S)
为集合
S
需要的最小分组数。
那么
要枚举全集的所有子集的所有子集,时间复杂度是多少呢?答案是 O(3n) ( 结果我记成枚举一个集合的所有子集要 O(3n) )
全集的所有子集有 ∑nk=0Ckn 个,每个子集的子集有 ∑ki=0Cik=2k 个,总的有:
∑k=0nCkn2k=∑k=0nCkn1n−k2k=(1+2)n=3n
(二项式定理)
先预处理好每个集合可否分为一组,然后就DP了。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
bool g[20][20];
int f[1<<17],d[1<<17];
int main(){
int n,m,k;
cin>>n>>m>>k;
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
g[a][b]=g[b][a]=1;
}
for(int i=0;i<n;i++) f[1<<i]=0;
for(int i=1;i<(1<<n);i++)
for(int j=16;j>=0;j--) if(i&(1<<j)){
int s=i^(1<<j);
f[i]=f[s];
for(int k=16;k>=0;k--)
if((1<<k)&s) if(g[j+1][k+1]) f[i]++;
break;
}
for(int i=1;i<(1<<n);i++) d[i]=1e9;
for(int i=1;i<(1<<n);i++)
for(int s=i;s;s=(s-1)&i)
if(f[s]<=k) d[i]=min(d[i],d[i^s]+1);
printf("%d\n",d[(1<<n)-1]);
}