Description
明天就要和所有M&F成员去唐山旅游了,但是SF面馆特派员还有N件事情没有做完成,做这件事还有M个限制条件,每个限制条件均为:事件A必须在事件B之前完成。现在SF面馆特派员想知道做完这N件事一共有多少种方法。
Input
第一行两个整数N,M。接下来M行,每行两个数Ai,Bi,表示事件Ai必须在Bi之前完成。
Output
一个整数,完成N件事共有多少种方法。
Sample Input
3 2
1 3
2 3
Sample Output
2
Hint
N≤17; M≤400; 1S; 256M
题解
(N≤17)的数据着实有些小,于是我们就可以用位运算。用二进制来记录状态,(1)表示未选(或可选),(0)表示已选,(f[])表示该状态的方案数,加上记忆化后貌似跑得更快,最后加上拓扑排序的模板即可。
#include<iostream>
#include<cstdio>
using namespace std;
const int N=20,M=500,N2=1<<18;
int n,m,nout[N],in[N],out[N][M];
long long f[N2];
void Dfs(int nw)
{
if(f[nw]) return;
int YH;
for(int i=1;i<=n;++i)
if((!in[i])&&(nw&(1<<(i-1))))//可选
{
YH=nw&(~(1<<(i-1)));//选
for(int j=1;j<=nout[i];++j) --in[out[i][j]];
Dfs(YH);
for(int j=1;j<=nout[i];++j) ++in[out[i][j]];
f[nw]+=f[YH];
}
}
int main()
{
scanf("%d%d",&n,&m);
int a,b;
for(int i=1;i<=m;++i)
scanf("%d%d",&a,&b),
++nout[a],out[a][nout[a]]=b,++in[b];
int YH=(1<<n)-1;
f[0]=1;
Dfs(YH),
cout<<f[YH]<<endl;
return 0;
}