【描述】
给定一个有向图,求拓扑排序生成的序列数
【分析】
我们知道当所有儿子节点排好序的时候,父节点就排好序了。
这里我们定义一种状态:状态s的二进制位上的1表示此点已经排好序了。
例如:s=6时,化为二进制s=110,表示第2、3个点已经排好序了。
所以父节点的状态可以由子节点转移而来。
用son[i]表示节点i可以进行转移的合法状态,f[s]表示状态为s的方法数。
然后枚举所有的状态,然后在此状态中找二进制位上是0的点,如果这个点要求的合法状态是当前状态的子状态,那么可以由当前状态转移到把第i位设为1的状态。
详见代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> #include<cmath> #include<algorithm> using namespace std; typedef long long ll; #define FILE "read" #define up(i,j,n) for(ll i=j;i<=n;i++) namespace INIT{ char buf[1<<15],*fs,*ft; inline char getc() {return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;} inline ll read() { ll x=0,f=1; char ch=getc(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getc();} while(isdigit(ch)) {x=x*10+ch-'0'; ch=getc();} return x*f; } }using namespace INIT; ll n,m,son[30],f[1<<17]; int main(){ freopen(FILE".in","r",stdin); freopen(FILE".out","w",stdout); n=read(); m=read(); up(i,1,m) {ll x=read(),y=read(); son[x]|=(1<<(y-1));} f[0]=1; up(s,0,(1<<n)-1) if(f[s]>0) up(i,1,n) if((s&son[i])==son[i]&&(s&(1<<(i-1)))==0) f[s|(1<<(i-1))]+=f[s]; printf("%lld ",f[(1<<n)-1]); return 0; }