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;
}
posted @ 2021-03-01 18:29  zYzYzYzYz  阅读(67)  评论(0编辑  收藏  举报