虽然标题是图论,我感觉和图论有关系,但是和dp关系更密切。
我们发现这个不相交环其实就是把原图造成二分图的一个完全匹配。
比如如果有一个i->j的边我们就在i->j'的二分图中建一个边。
我们可以容易知道二分图的完全匹配就是不相交环覆盖的方案数。
那么就可以用集合dp在O(n2^n)中找出所有完全匹配就可以了。
就是枚举一下这个集合然后找一个固定元素出来求一下贡献就可以了。
转移时O(n)的状态集合数是O(2^n)的。
dp[i][j]表示第i位已经匹配了i个数,这i个数可以用二进制j表示,n个数每个数都匹配一个不是自身的数,就是全集(1<<n)-1.当然任意两个数不能匹配相同的数。不然结果为0,开始dp[0][0]=1。
细思极巧。
#include <bits\stdc++.h>
#include <math.h>
using namespace std;
const int mod=998244353;
vector<int>a[25];
int f[25][(1<<20)+5];
int main()
{
int n,m,u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
a[u].push_back(v);
}
f[0][0] = 1;
for (int i = 1; i <= n; i ++){
for (int j = 0; j < 1<<n; j ++)
if (__builtin_popcount(j) == i){
for (int k =0; k<a[i].size(); k++){
int v = a[i][k];
if ((j>>v-1)&1)
f[i][j] = (f[i][j]+f[i-1][j^(1<<v-1)])%mod;
}
//cout<<i<<" "<<j<<" "<<f[i][j]<<endl; 输出看一下更容易理解
}
}
printf("%d\n", f[n][(1<<n)-1]);
return 0;
}