ZOJ-3305* 二进制子集划分

3305:有N中调料,M种配方。调料每种只能用一次,求能实现多少种配方。
即统计能划分出多少备选子集。


思路:本题代码参考自http://akheyun.blog.163.com/blog/static/13824927620102183300293/
核心思想是二进制表现。
x = (x - 1) & st 实现了子集遍历
比如 st=1110
1101 & 1110 = 1100
1011 & 1110 = 1010
1001 & 1110 = 1000
0111 & 1110 = 0110
0101 & 1110 = 0100
0011 & 1110 = 0010
0001 & 1110 = 0000

联想起求一个数的二进制中1有多少。
用到 x = (x-1) & x 这每次会使一个1变为0 用时可以小于O(n)


#include<iostream>
using namespace std;
#include<stdio.h>
#include<memory.h>

const int M = 1 << 16 + 1;
bool a[M];
int dp[M];

int solve(int st)
{
int i;
if (dp[st] != -1) return dp[st]; //已经计算过,直接返回
dp[st] = 0;
for (int x = st; x != 0; x = (x - 1) & st)
{
//不使用x能达到的配方数和使用x后配方数的最大值
if (a[x])
dp[st] = max(dp[st], solve(st - x) + 1);
}
return dp[st];
}



int main ()
{

int i, j, n, m, k, p;
while (scanf("%d%d", &n, &m) != EOF)
{
memset(a, false, sizeof(a));
memset(dp, -1, sizeof(dp));

for (i = 0; i < m; i++)
{
scanf("%d", &k);
j = 0;
while (k--)
{
scanf("%d", &p);
j += (1 << (p - 1)); //每种配方用2进制表示
}
a[j] = 1;
}

//初始全为1传入
printf("%d\n", solve((1 << n) - 1));
}

}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值