poj 2441 状态压缩+滚动数组+剪枝DP

题意:

有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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值