题意:
给定n个点m条边的无向图,
要求删掉若干边,使得图为若干个连通块,满足每个连通块都是完全图,
问最少的连通块数量是多少。
数据范围:n<=18,m<=n(n-1)/2
解法:
n<=18,考虑状压dp:
令d[i]表示点集为i时,满足题目条件的最小连通块数量.
转移方程:
d[i]=1,此时需满足i自身是完全图.
d[i]=d[i-j]+d[j],其中j是i的子集.
code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int d[1<<20];
int e[20];//e[i]为点i的边集
int n,m;
signed main(){
ios::sync_with_stdio(0);
cin>>n>>m;
for(int i=0;i<n;i++)e[i]|=(1<<i);
for(int i=1;i<=m;i++){
int a,b;cin>>a>>b;
a--,b--;
e[a]|=(1<<b);
e[b]|=(1<<a);
}
//init
for(int i=0;i<(1<<n);i++){
d[i]=1e9;
}
d[0]=0;
//dp
for(int i=1;i<(1<<n);i++){
//判断集合是否本身是一个完全图
int ok=1;
for(int j=0;j<n;j++){
if(i>>j&1){
if((e[j]&i)!=i){
ok=0;
}
}
}
if(ok)d[i]=1;
//枚举当前集合是从何组合的
for(int j=i;j;j=((j-1)&i)){
//枚举子集
d[i]=min(d[i],d[i-j]+d[j]);//d[i]由(i-j)和j组成.
}
}
cout<<d[(1<<n)-1]<<endl;
return 0;
}