安排公牛
Description
Fammer Johnson的公牛们非常喜欢打篮球。但它们都不愿意一起打篮球,因为所有公牛都认为其他的牛弱爆了。Fammer Johnson有N头牛(我们把它们编号为1~N)和M个牛棚(我们把它们编号为1~M),这些牛棚就是公牛们的篮球场。但FJ的公牛非常挑剔,它们只会在自己喜欢的牛棚打球,并且都不愿意和其他的牛共享牛棚。
因此安排他的公牛对Famer Johnson来说是一项艰难的任务,他希望得到你的帮助。当然,找到一种方案是容易的,但你的任务是找出一共有多少种方案。
一个方案是一种使得所有公牛都能在它喜欢的牛棚中快乐玩耍,并且没有两只牛共享一个牛棚的安排方式。答案不会超过32位无符号整数范围。
Input
第一行有两个正整数N,M(1<=N,M<=20)。
接下来有N行,第i行的开头有一个正整数P(1<=P<=M),代表第i头牛喜欢的牛棚数量。接下来有P个正整数,即P个牛棚的编号。
Output
输出一行一个正整数,即方案总数。
Sample Input
3 4
2 1 4
2 1 3
2 2 4
Sample Output
4
Source
POJ 2441
位运算, 动态规划 ,状态压缩
Solution
又是一道状态压缩
首先用一个数组记录i头牛与j号棚之间的喜好关系(bool就行),喜欢为1,不喜欢为0
枚举每一头牛,即对于每一头牛可能的分配方式(其实就是枚举每一种可能的分配方式),然后用当前牛的喜好情况去更新当前枚举的分配方式(普通的位运算实现即可),注意:为了避免重复每一种用来更新的分配方式更新完后就清0,只有它有值时才用来更新
Code
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define LL long long
using namespace std;
LL n, m, q, a, ans;
bool lik[30][30];
LL dp[(1 << 20)];
int main() {
freopen("examnine.in", "r", stdin);
freopen("examnine.out", "w", stdout);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &q);
for (int j = 1; j <= q; ++j)
scanf("%d", &a), lik[i][a - 1] = 1;
}
dp[0] = 1;
for (int i = 1; i <= n; ++i)
for (int j = (1 << m) - 1; j >= 0; j--) {
if (!dp[j]) continue;
for (int k = 0; k < m; ++k) {
if (j & (1 << k)) continue;
if (!lik[i][k]) continue;
int temp = (j | (1 << k));
dp[temp] += dp[j];
}
dp[j] = 0;
}
for (int i = 0; i < (1 << m); ++i) ans += dp[i];
printf("%d\n", ans);
return 0;
}
Summary
后面参看了一下题解,意识到了些玄学问题,就想出了转移方程,但是还是WA了……
对着数据调了一会儿瞬间发现bug:dp初值都没赋,什么都没有更新,直接输出0
加上dp[0] = 1,就A了