而
n
n
n又只有
22
22
22,所以我们可以用一个二进制数来表示点集的状态,用
f
i
f_i
fi表示点
i
i
i相连的点集,那么只要目前的点集的
f
i
f_i
fi的或和为整个序列,那么就保证向外与整个图联通,而自身内部联通可以用
B
F
S
BFS
BFS来验证。普通的验证理论复杂度为
O
(
n
2
)
O(n^2)
O(n2),但是今天的一个骚操作可以把这个复杂度将为
O
(
n
)
O(n)
O(n)
因为每个点只能入队一次,因此我们可以用二进制来记录队列里的点和
v
i
s
vis
vis数组的状况。那么我们每次选择队列状态的最低非零为进行转移,直接用位运算算出下面的状态。由于只关心点集内部的情况,所以要
&
i
\&i
&i(
i
i
i表示点集),又因为进过队的不再进,所以还要
&
\&
&~
v
i
s
vis
vis,那么这样一直转移,知道
Q
Q
Q为
0
0
0 的时候,如果
v
i
s
vis
vis已经能表示整个点集,也就是vis==i,那么这个状态内部就是连通的,也就是可行的。
因此每次枚举,广搜,取最小的点集即可。
#include<bits/stdc++.h>
#define MAXN 4200000
using namespace std;
int read(){
char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,m,ans,f[25],lst[MAXN],num[MAXN];
int main()
{
n=read();m=read();
for(int i=1;i<(1<<n);i++)num[i]=num[i>>1]+(i&1);
for(int i=1;i<(1<<n);i++)lst[i]=(i&1)?1:lst[i>>1]+1;
for(int i=1;i<=n;i++) f[i]|=(1<<i-1);
for(int i=1;i<=m;i++){
int x=read(),y=read();
f[x]|=(1<<y-1);f[y]|=(1<<x-1);
}
for(int i=1;i<=n;i++)
if(f[i]!=(1<<n)-1) ans=(1<<i)-1; //如果每个点都能直接与所有点连通,那么这步后ans=0
for(int i=1;i<(1<<n);i++){
int cheq=0,vis=0,Q=i&-i;
for(int j=1;j<=n;j++)
if((i&(1<<j-1))) cheq|=f[j];
if(cheq!=(1<<n)-1) continue; //向外不能整个连接就弹出
while(Q){
int p=lst[Q];
Q^=(1<<p-1);vis|=(1<<p-1);
Q|=f[p]&i&~vis;
}
if(vis==i&&num[i]<num[ans]) ans=i;
}
printf("%d\n",num[ans]);
for(int p=1;ans;ans/=2,p++)
if(ans&1) printf("%d ",p);
return 0;
}