一、题目
二、解法
看到
n
≤
10
n\leq 10
n≤10,自然而然第想到了状压。
定义
d
p
[
s
1
]
[
s
2
]
dp[s1][s2]
dp[s1][s2]为联通情况为
s
1
s1
s1,叶子的情况为
s
2
s2
s2,初始化
d
p
[
2
i
−
1
]
[
2
i
−
1
]
dp[2^{i-1}][2^{i-1}]
dp[2i−1][2i−1]为
1
1
1,然后转移也不难。
但是这道题真的这么简单吗?当然不是,考虑下面一种情况。
所以状压会有副作用,顺序不同的同种方案会被多次计算,不仅如此,不同的根会导致相同的方案多次计算,所以我们要去规定一种计算法则,这里我们规定
s
2
s2
s2的最高位只能是最新加入的。
#include <cstdio>
#include <vector>
using namespace std;
#define LL long long
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^'0'),c=getchar();
return x*flag;
}
int n,m,k,p,tot,f[15],cnt[1<<10];
LL dp[1<<10][1<<10],ans;
struct edge
{
int v,next;
}e[205];
void init()
{
for(int s=1;s<=p;s++)
{
for(int i=1;i<=n;i++)
if(s&(1<<i-1))
cnt[s]++;
}
for(int i=1;i<=n;i++)//初始化
dp[(1<<i-1)][(1<<i-1)]=1;
}
int main()
{
n=read();m=read();k=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
e[++tot]=edge{v,f[u]},f[u]=tot;
e[++tot]=edge{u,f[v]},f[v]=tot;
}
p=(1<<n)-1;
init();
for(int s1=1;s1<=p;s1++)
for(int s2=s1;s2;--s2&=s1)//要保证s2是s1的子集
{
if(dp[s1][s2])
{
for(int i=1;i<=n;i++)
if(s1&(1<<i-1))
{
for(int j=f[i];j;j=e[j].next)
{
int v=e[j].v,now=0;
if(s1&(1<<v-1)) continue;
if(cnt[s1]==1) now=s1|(1<<v-1);//特判只有根(度数为0)
else now=s2&~(1<<i-1)|(1<<v-1);//取反后并,消掉该位
if(!(now>>v)) dp[s1|(1<<v-1)][now]+=dp[s1][s2];
}
}
}
}
for(int s=1;s<=p;s++)
{
if(cnt[s]==k)
ans+=dp[p][s];
}
printf("%lld\n",ans);
}