ABC187F 题解
ABC187F
简要题意
将一个 \(n\) 个点 \(m\) 条边的无向图分成 \(k\) 个部分,使得每个部分都是一个完全图,最小化 \(k\) 。
样例
3 2 //点数 边数
1 2 //一条边连接的两个节点
1 3 //同上
2 //k的最小值
分成 \(\{1,2\}\{3\}\) 或 \(\{1,3\}\{2\}\) 。
数据范围
\(1\le n\le18\) , \(0\le m\le\frac{n\times(n-1)}{2}\)。
1s,256MB。
题解
发现 \(n\) 很小,考虑状压。
设 f[i]
表示选的点集为 \(i\) 时的答案。
设 out[i]
表示点集 \(i\) 中的所有点的出边组成的集合的交集,令 \(out[0]=2^n-1\)。
设 edge[i]
表示节点 \(i\) 的出边组成的集合,初始时令 \(edge[i]=2^{i-1}\)。
对于每一个点集 \(i\) ,如果 \(out[i]\&i=i\) 则说明 \(i\) 是一个完全图。
枚举 \(i\) 的子集 \(j\) ,如果 \(j\) 是一个完全图那么 \(f[i]=min\{f[i\hat{}j]\}+1\)。
#include<bits/stdc++.h>
using namespace std;
int n,m,edge[20],End;
int f[1050000],out[1050000],Log[1050000];
inline int read()
{
int x=0,w=0;char ch=0;
while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return w?-x:x;
}
int main()
{
End=1<<(n=read());m=read();
for(int i=1;i<=m;i++){
int x=read(),y=read();
edge[x]|=(1<<(y-1));//更新出边集合
edge[y]|=(1<<(x-1));
}
for(int i=1;i<=n;i++){
Log[1<<(i-1)]=i;
edge[i]|=(1<<(i-1));//出边集合中加入自己
}
memset(f,0x3f,sizeof f);
f[0]=0;out[0]=End-1;
for(int i=1;i<End;i++){
out[i]=(out[i^(i&-i)]&edge[Log[i&-i]]);//更新出边集合
if((out[i]&i)==i){f[i]=1;continue;}//如果 i 本身是一个完全图的话就不用枚举子集了
for(int j=i;j;j=(j-1)&i)//枚举 i 的子集
if((out[j]&j)==j)//判断 j 是否是完全图
f[i]=min(f[i],f[i^j]+1);
}
cout<<f[End-1]<<'\n';
return 0;
}