JYOJ 1388 旅游 状压DP:拓扑排序的方案数

title

LUOGU 2019
JYOJ 1388
描述 Description

明天就要和所有M&F成员去唐山旅游了,但是SF面馆特派员还有N件事情没有做完,做这些事情还有M个限制条件,每个限制条件均为:事件A必须在事件B之前完成。现在SF面馆特派员想知道,他做完这N件事一共有多少种方法。

输入格式 Input Format

第一行两个整数N、M。
接下来M行 每行两个数Ai、Bi,表示事件Ai必须在事件Bi之前完成。

输出格式 Output Format

一个整数,表示做完这N件事一共有多少种方法。

样例输入 Sample Input

3 2
1 3
2 3

样例输出 Sample Output

2

时间限制 Time Limitation

1s

注释 Hint

N<=17;
M<=25。
方法1:1,2,3;
方法2:2,1,3。

来源 Source

noip模拟赛
拓扑排序的方案数

analysis

我们可以知道,当所有儿子节点的都排列好顺序时,父亲节点的排列顺序也就确定了。

我们可以定义一种状态:状态 s s s的二进制位上的 1 1 1表示此点已经排好序了。

例如: s = 6 s=6 s=6时,化为二进制 s = 110 s=110 s=110,表示第 2 、 3 2、3 23个点已经排好序了。

所以父节点的状态可以由子节点转移而来。

s o n [ i ] son[i] son[i]表示节点 i i i可以进行转移的合法状态, f [ s ] f[s] f[s]表示状态为 s s s的方法数。

然后枚举所有的状态,然后在此状态中找二进制位上是 0 0 0的点,即 ! ( s & ( 1 < < i − 1 ) ) !(s\&(1<<i-1)) !(s&(1<<i1)),如果这个点要求的合法状态是当前状态的子状态,即 ( s o n [ i ] & s ) = = s o n [ i ] (son[i]\&s)==son[i] (son[i]&s)==son[i],那么可以由当前状态转移到把第 i i i位设为 1 1 1的状态。

code

#include
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
ll son[30],f[1<<17];
int main()
{
	ll n,m;read(n);read(m);
	for (ll i=1; i<=m; ++i)
	{
		ll x,y;
		read(x);read(y);
		son[x]|=1<<y-1;
	}
	f[0]=1;
	for (ll s=0; s<=(1<<n)-1; ++s) if (f[s]>0)
		for (ll i=1; i<=n; ++i)
			if ((son[i]&s)==son[i] && !(s&(1<<i-1)))
				f[s|(1<<i-1)]+=f[s];
	printf("%lld\n",f[(1<<n)-1]);
	return 0;
}

你可能感兴趣的:(JYOJ 1388 旅游 状压DP:拓扑排序的方案数)