安排公牛

安排公牛

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

考试的题,考试的时候写了一个搜索,70分,实在是想不出转移方程

后面参看了一下题解,意识到了些玄学问题,就想出了转移方程,但是还是WA了……

对着数据调了一会儿瞬间发现bug:dp初值都没赋,什么都没有更新,直接输出0

加上dp[0] = 1,就A了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值