考虑状压dp:f[u][p]表示当前点为u,状态为p的方案数,用记忆化搜索来实现。
为了避免重复情况的出现,我们需要再枚举一个状态中的最小节点:在后来的所有进入环中的节点均需大于该状态的最小节点。
由于一个环,可以正着走和倒着走,所以最后方案数要除以2。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=19;
int n,m,x,y,ans;
int a[N][N],bin[N],f[N][1<<N];
bool vis[N][1<<N];
int dfs(int u,int p)
{
if (vis[u][p]) return f[u][p];
vis[u][p]=true;
int sum=__builtin_popcountll(p),minn=__builtin_ffsll(p)-1;
for (register int i=0; i<n; ++i)
if (a[u][i])
{
if (bin[i]&p)
{
if (sum>2 && i==minn) f[u][p]++;
}
else
{
if (i>minn) f[u][p]+=dfs(i,p|bin[i]);
}
}
return f[u][p];
}
signed main(){
scanf("%lld%lld",&n,&m);
for (register int i=1; i<=m; ++i)
{
scanf("%lld%lld",&x,&y);
x--; y--;
a[x][y]=a[y][x]=1;
}
for (register int i=0; i<=18; ++i) bin[i]=1ll<<i;
for (register int i=0; i<n; ++i) ans+=dfs(i,bin[i]);
printf("%lld\n",ans>>1ll);
return 0;
}