题意
给nnn个点(n≤18n\leq18n≤18),mmm条边(m≤n∗(n−1)2m\leq\frac{n*(n-1)}{2}m≤2n∗(n−1))你一个简单无向图,删去一些边(可以是0),使得图满足以下性质:
- 任意两点aaa,bbb,如果aaa,bbb连通,那么aaa,bbb之间有边。
求满足条件最少的连通块数量。
思路
题目数据很小,状压走起!
首先我们设fvf_vfv表示当顶点集合为vvv时,最少的连通块数量。
然后我们先暴力枚举点集vvv,判断这个点集vvv是否为完全图。
此时我们想怎么转移。
我们可以发现当v′v'v′为vvv的子集时,fv=min(fv′+fv−v′)f_v=min(f_{v'}+f_{v-v'})fv=min(fv′+fv−v′)。
所以此时我们就要枚举v′v'v′。
我们先把v′=vv'=vv′=v,然后我们接下来每次都把v′=(v′−1)&vv'=(v'-1)\&vv′=(v′−1)&v,此时(v′−1)&v(v'-1)\&v(v′−1)&v肯定是vvv的子集,因为如果v′−1v'-1v′−1中二进制下有一位为111且vvv的这一位为000,那么在(v′−1)&v(v'-1)\&v(v′−1)&v的时候这个111就不见了,因为1&0=01\&0=01&0=0。
接下来我们来算一下时间复杂度。
在枚举v′v'v′的时候,因为vvv和v′v'v′的在二进制下每一位的关系只有三种情况,1/01/01/0,1/11/11/1,0/00/00/0,也就是说,把所有枚举的情况乘起来,就是3n3^n3n,虽然3n≥3×1093^n\geq 3 \times 10^93n≥3×109,但是因为常数很小,所以还是可以过。
Code
#include<bits/stdc++.h>
using namespace std;
int n,m,mp[25][25],f[2621445];
int main() {
int u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d",&u,&v),mp[u][v]=mp[v][u]=1;
bool flag;
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=(1<<n)-1;i++) {
flag=true;
for(int j=1;j<n;j++)
if((i>>(j-1))&1)
for(int l=j+1;l<=n;l++)
if((i>>(l-1))&1&&(!mp[j][l])) {
flag=false;
break;
}
if(flag)
f[i]=1;
}
for(int i=1;i<=(1<<n)-1;i++)
for(int j=i;j;j=(j-1)&i)
f[i]=min(f[i],f[j]+f[i^j]);
printf("%d",f[(1<<n)-1]);
return 0;
}