CF11D
A Simple Task
状压 DP。
定义状态比较有技巧,
于是乎,我们枚举每个状态,再枚举它可到达的终点有哪些,再枚举新的点看这种路径是否能扩展。然后新的点需要是终点,所以不能为编号最小的点。如果说可以形成环,自然统计进入答案,否则,我们利用此更新状态。
时间复杂度
这里去重不多做解释,即
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
using namespace std;
const ll N=19,I=1<<N;
ll n,m,u,v,ans;
ll f[I+5][N+5];
bool edge[N+5][N+5];
inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*f;
}
void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {
do{buf[++len]=x%10+48;x/=10;}while(x);
}
else {
putchar('-');
do{buf[++len]=-(x%10)+48;x/=10;}while(x);
}
while(len>=0) putchar(buf[len--]);
}
int main() {
n=read();m=read();
for(ll i=1;i<=m;i++) {
u=read();v=read();
edge[u][v]=edge[v][u]=1;
}
for(ll i=1;i<=n;i++) {
f[1<<(i-1)][i]=1;
}
for(ll i=0;i<(1<<n);i++) {
for(ll j=1;j<=n;j++) {
if(!f[i][j]) continue;
for(ll k=1;k<=n;k++) {
if(!edge[j][k]) continue;
if((1<<(k-1))<(i&-i)) continue;
if((1<<(k-1))&i) {
if((1<<(k-1))==(i&-i)) {
ans+=f[i][j];
}
}
else {
f[i|(1<<(k-1))][k]+=f[i][j];
}
}
}
}
write((ans-m)/2);
return 0;
}