拓扑排序方案数的求法

【描述】

给定一个有向图,求拓扑排序生成的序列数

【分析】

我们知道当所有儿子节点排好序的时候,父节点就排好序了。

这里我们定义一种状态:状态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\n",f[(1<<n)-1]);
	return 0;
}



posted @ 2016-11-16 08:34  chty  阅读(1505)  评论(0编辑  收藏  举报