方法:状压
解析:
N这么小的范围,看到就忍不住想要压一压- -!
先想想对半分的方案数。
C13262
大概500W吧挺小的。
现在看我们的转移辣。
如果O(n)的话是作死!
所以如何降到O(1)呢?
我们把每一个点的连向的点压成一个二进制数。
然后转移我们就可以做到删去原来的加上新的。
这个复杂度是O(1的个数)
但是如果我们预处理出二进制数中1的个数,那么转移就O(1)辣,这题就可以水过去啦
注意:预处理的话我们只能处理1<<13范围内的,再往上半劈就好了。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 27
using namespace std;
int n,m;
int edge[N];
int cnt[(1<<13)+10];
int s1,s2;
int ans,print;
void init()
{
for(int i=0;i<=(1<<13);i++)
{
for(int j=13;j>=0;j--)
{
if(i&(1<<j))
cnt[i]++;
}
}
}
int get_num(int x)
{
return cnt[x>>13]+cnt[(x-((x>>13)<<13))];
}
void dfs(int pos,int all,int sum)
{
if(all==n/2)
{
if(sum<ans)
{
ans=sum;
print=s1;
}
return;
}
for(int i=pos+1;i<=n;i++)
{
int tmp=get_num(edge[i]|s1);
int tmp2=get_num(edge[i]|s2);
s1|=(1<<(i-1));
s2^=(1<<(i-1));
dfs(i,all+1,sum-tmp2+tmp);
s1^=(1<<(i-1));
s2|=(1<<(i-1));
}
}
int main()
{
scanf("%d%d",&n,&m);
s1=0,ans=0x3f3f3f3f,s2=(1<<n)-1;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
edge[x]|=(1<<(y-1));
edge[y]|=(1<<(x-1));
}
init();
dfs(0,0,0);
for(int i=1;i<=n;i++)
{
if(print&(1<<(i-1)))
{
printf("%d ",i);
}
}
puts("");
}