【状压dp】JZOJ_5230 队伍统计

本文介绍了一种使用状态压缩动态规划解决特定排列问题的方法,该问题涉及在考虑到一系列矛盾关系约束下,寻找所有可能的排列组合,同时不超过允许违背的矛盾关系数量。通过预处理违背条件并利用__builtin_popcount()函数,算法将时间复杂度降低到O(2^nkn),适用于小范围的n值。

题意

现在有nnn个人要排成一列,编号为1∼n1\sim n1n
mmm条矛盾关系(u,v)(u,v)(u,v),表示编号为uuu的人想要排在编号为vvv的人前面。
要使得队伍和谐,最多不能违背kkk条矛盾关系(即不能有超过kkk条矛盾关系(u,v)(u,v)(u,v),满足最后vvv排在了uuu前面)。问有多少合法的排列。答案对1e9+71e9+71e9+7取模。

思路

nnn的范围很小,最大才有202020,考虑状态压缩dpdpdp

f[S][i]f[S][i]f[S][i]为当前选了的人集合为SSS,已经违背了iii条关系。

如果我们暴力转移,那么时间复杂度为O(2nn2k)O(2^nn^2k)O(2nn2k)

由于关系是没有重复的,我们可以预处理一下违背的转移条件,设behind[i]behind[i]behind[i]为原来应该站在iii后面的人,在转移时计算出behind[i] & Sbehind[i]\ \&\ Sbehind[i] & S111的个数,代表有这么多本来应站在后面的人站在前面了。

时间复杂度降成O(2nkn)O(2^nkn)O(2nkn)

代码

#include<cstdio>

const int P = 1e9 + 7;
int n, m, k;
int f[1048576][21], behind[21];

int main() {
	scanf("%d %d %d", &n, &m, &k);
	for (int i = 1, u, v; i <= m; i++) {
		scanf("%d %d", &u, &v);
		behind[u] |= 1 << v - 1;
	}
	int t = 1 << n;
	f[0][0] = 1;
	for (int i = 0; i < t; i++)
		for (int s = 0; s <= k; s++)
			if (f[i][s])
				for (int j = 1; j <= n; j++) {
					if (i & 1 << j - 1) continue;
					int need = __builtin_popcount(behind[j] & i);//自带函数计算1的个数,最好手打
					if (need + s > k) continue;
					f[i | 1 << j - 1][s + need] = (f[i | 1 << j - 1][s + need] + f[i][s]) % P;
				}
	int ans = 0;
	for (int i = 0; i <= k; i++)
		ans = (ans + f[(1 << n) - 1][i]) % P;
	printf("%d", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值