思路:状压dp
根据
n
n
n的范围,很容易判断要用状压
d
p
dp
dp,我们可以用一个二进制数来表示当前这
n
n
n个点,哪些点被访问,例如:
11001
11001
11001表示点
1
,
4
,
5
1,4,5
1,4,5被访问
那么我们定义
d
p
[
i
]
[
u
]
dp[i][u]
dp[i][u]表示经过的点的状态为
i
i
i且最后一个点为
u
u
u的方案数,那我们考虑起点就是状态
i
i
i这个二进制数的最低位的
1
1
1代表的点
如何求这个起点呢?我们想到
l
o
w
b
i
t
lowbit
lowbit函数求的是一个二进制数的最低位的
1
1
1代表的值,在这基础上取对数即为这个最低位的
1
1
1的位数,即:
int lowbit(int x){
return (int)log2((x)&(-x));
}
那在
d
p
[
i
]
[
u
]
dp[i][u]
dp[i][u]的基础上,求出起点
k
=
l
o
w
b
i
t
(
i
)
k=lowbit(i)
k=lowbit(i),如果当前点
u
u
u与起点
k
k
k连通,那么
a
n
s
+
=
d
p
[
i
]
[
u
]
ans+=dp[i][u]
ans+=dp[i][u]
接下来枚举
v
∈
a
[
u
]
v\in a[u]
v∈a[u],
v
v
v不在状态
i
i
i中且
v
v
v不会为起点(
v
>
k
v>k
v>k),进行状态转移:
d
p
[
i
∣
(
1
<
<
v
)
]
[
v
]
+
=
d
p
[
i
]
[
u
]
dp[i|(1<<v)][v]+=dp[i][u]
dp[i∣(1<<v)][v]+=dp[i][u]
由于在计算过程中,我们把图当作了有向图,一个环被计算了两次,并且每条边也被当作了环,也要减去
#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define endl '\n';
typedef long long ll;
typedef pair<ll,ll> PII;
const int N=1e6+10;
int dp[1<<20][20],n,m,a[20][20];
int lowbit(int x){
return (int)log2((x)&(-x));
}
int main(){
IOS;
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
a[u-1][v-1]=a[v-1][u-1]=1;
}
for(int i=0;i<n;i++) dp[1<<i][i]=1;
ll ans=0;
for(int i=1;i<(1<<n);i++){
int k=lowbit(i);
for(int u=0;u<n;u++){
if(!((i>>u)&1)) continue;
if(a[u][k]) ans+=dp[i][u];
for(int v=k+1;v<n;v++){
if(!a[u][v]||((i>>v)&1)) continue;
dp[i|(1<<v)][v]+=dp[i][u];
}
}
}
cout<<(ans-m)/2<<endl;
return 0;
}