【状压dp】AT_dp_o Matching 题解

题意

问题描述有 N N N名男性和 N N N名女性,编号分别为 1 , 2 , … , N 1,2,…,N 1,2,,N

对于每一对 i , j ( 1 ≤ i , j ≤ N ) i,j(1≤i,j≤N) i,j1i,jN,男性 i i i 和女性 j j j 的兼容性被表示为整数 a i , j a_{i,j} ai,j 。 如果 $a_{i,j}=1,则男性 i i i 和女性 j j j 兼容;如果 a i , j = 0 a_{i,j} =0 ai,j=0,则它们不兼容。

Taro试图组成 N N N对,每对由一个男性和一个女性组成,二者必须各自属于一对。

求Taro能够组成 N N N对的方式数量,取模 1 0 9 + 7 10^9+7 109+7

【样例输入】

3
0 1 1
1 0 1
1 1 1

【样例输出】

3

【样例解释】

  • ( 1 , 2 ) , ( 2 , 1 ) , ( 3 , 3 ) (1,2),(2,1),(3,3) (1,2),(2,1),(3,3)
  • ( 1 , 2 ) , ( 2 , 3 ) , ( 3 , 1 ) (1,2),(2,3),(3,1) (1,2),(2,3),(3,1)
  • ( 1 , 3 ) , ( 2 , 1 ) , ( 3 , 2 ) (1,3),(2,1),(3,2) (1,3),(2,1),(3,2)

思路

d p i dp_i dpi 表示状态为 i i i 对应的当前已经被配对到的女性,其中:

i = 2 0 a 0 + 2 1 a 1 + … 2 n a n , a i = 0 / 1 i = 2^0a_0 +2^1a_1 + \dots2^na_n,a_i = 0/1 i=20a0+21a1+2nan,ai=0/1

如果 d p i dp_i dpi 表示 1 1 1 则代表第 i i i 个女生去过。

我们当前要配对的是第 c n t cnt cnt 个男生,因为已经配对过 ∑ i = 0 n a i \sum_{i=0}^{n}{a_i} i=0nai,所以有当前配对的是第 ∑ i = 0 n a i + 1 \sum_{i=0}^{n}{a_i} + 1 i=0nai+1 个。__builtin_popcount(i)代表 i i i 在二进制中一的数量。

枚举当前配对的女生 j j j,再转移即可。

代码

#include<bits/stdc++.h>
#define int long long
const int p = 1000000007;
using namespace std;
int n,a[25][25];
int dp[(1 << 21) + 10],ans = 0;
signed main() {
	scanf("%lld",&n);
	for(int i = 1;i <= n;i++) {
		for(int j = 1;j <= n;j++) {
			scanf("%lld",&a[i][j]);
		}
	}
	dp[0] = 1;
	for(int i = 0;i < (1 << n);i++) {
		int cnt = __builtin_popcount(i) + 1;
		for(int j = 1;j <= n;j++) {
			if(a[cnt][j] and (i & (1 << (j - 1))) == 0) {
				dp[i | (1 << (j - 1))] += dp[i];
				dp[i | (1 << (j - 1))] %= p;
			}
		}
		//printf("%lld\n",dp[i]);
	}
	printf("%lld\n",dp[(1 << n) - 1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值