题意:
有n(20)只牛和m(20)个篮球场,每个篮球场只能一只牛打,然后每只牛都有自己喜好的篮球场。
现在问有多少种分配方法使得每只牛都能在自己喜欢的篮球场打球,并且这个球场只有它一只牛。
解析:
首先是状压:
dp[ i ] [ state ] 表示的是第i只牛球场状态为state时的分配方法数。
状态转移方程也很好推:
dp[ i ] [ state | (1 << (v - 1)) ] = ∑ dp[ i - 1 ] [ state ]。
其中v代表的是当前牛i喜欢的球场v。
然后超内存。
改滚动数组:
dp[i & 1][state | (1 << (v - 1))] += dp[1 - (i & 1)][state]。
然后超时。
加剪枝:
如果前一状态 dp[1 - (i & 1)][state] 是0,说明这个状态无法到达,也就不用花费一个遍历了。
把n只牛下的每种符合情况的状态加起来就ok了。
加的时候可以枚举,也可以用枚举k子集的方法。
k子集的意思是状态压缩完了的二进制里面有k个1的状态从小到大的枚举。
好像快不了多少,还re了一发。
int ans = 0;
int comb = (1 << n) - 1;
ans += dp[n & 1][comb];
while (comb < (1 << m))
{
int x = comb & -comb;
int y = comb + x;
comb = ((comb & ~y) / x >> 1) | y;
if (comb < (1 << m))
ans += dp[n & 1][comb];
}
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = (1 << 20) + 10;
int n, m;
int dp[2][maxn];
vector<int> cow[30];
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
while (~scanf("%d%d", &n, &m))
{
for (int i = 1; i <= n; i++)
{
cow[i].clear();
}
for (int i = 1; i <= n; i++)
{
int k, x;
scanf("%d", &k);
while (k--)
{
scanf("%d", &x);
cow[i].push_back(x);
}
}
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (int i = 1; i <= n; i++)
{
for (int state = 0; state < (1 << m); state++)
{
if (dp[1 - (i & 1)][state])///剪枝,不加tle
{
int sz = cow[i].size();
for (int j = 0; j < sz; j++)
{
int v = cow[i][j];
if (!(state & (1 << (v - 1))))
{
dp[i & 1][state | (1 << (v - 1))] += dp[1 - (i & 1)][state];
}
}
}
}
memset(dp[1 - (i & 1)], 0, sizeof(dp[1 - (i & 1)]));
}
int ans = 0;
for (int i = 0; i < (1 << m); i++)
{
ans += dp[n & 1][i];
}
printf("%d\n", ans);
}
return 0;
}