博客园 首页 私信博主 显示目录 隐藏目录 管理

BZOJ 1688: Disease Manangement (子集枚举)

                    Disease Manangement

Q - 枚举子集
Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u
 

Description

Alas! A set of D (1 <= D <= 15) diseases (numbered 1..D) is running through the farm. Farmer John would like to milk as many of his N (1 <= N <= 1,000) cows as possible. If the milked cows carry more than K (1 <= K <= D) different diseases among them, then the milk will be too contaminated and will have to be discarded in its entirety. Please help determine the largest number of cows FJ can milk without having to discard the milk.

Input

* Line 1: Three space-separated integers: N, D, and K 

* Lines 2..N+1: Line i+1 describes the diseases of cow i with a list of 1 or more space-separated integers. The first integer, d_i, is the count of cow i's diseases; the next d_i integers enumerate the actual diseases. Of course, the list is empty if d_i is 0. 

Output

* Line 1: M, the maximum number of cows which can be milked.

Sample Input

6 3 2
0
1 1
1 2
1 3
2 2 1
2 2 1

Sample Output

5

Hint

OUTPUT DETAILS: 

If FJ milks cows 1, 2, 3, 5, and 6, then the milk will have only two diseases (#1 and #2), which is no greater than K (2). 
 
 
输入案例的意思是
 有N头牛,它们可能患有D种病,现在从这些牛中选出若干头来,但选出来的牛患病的集合中不过超过K种病.
6 3 2    N,D,k
0        第一列是奶牛携带的疾病个数,后面表示疾病的种类
1 1      
1 2    
1 3
2 2 1
2 2 1    携带两种疾病,种类为1,2两类
 
题解:
这个题,枚举子集,真想说一句我真是日了狗了,搜集了一上午二进制法生成子集的东西才明白一点,现在算是会用一点了,简单介绍一下
这道题的的奶牛个数较小只有0-15,所以采用二进制的方法        
生成子集的二进制法:二进制中只有0和1两个数字。用二进制表示子集,从右到左第i位表示元素i是否在集合中。
        然后好对集合进行操作,通过为运算符实现,二元位运算:与(&),或(|),非(!)对应的是集合的交,或和对称差。

 

   x&=(x-1)表示将x转化为2进制,最低的为1的位变成0,看含有的1的个数。

这是“位运算”中的一种很经典的用法,“&”是“与”的意思。它具体点的意思就是把x的二进制表示数最右边的一个1变成0  例如:   
  e1:   
  x      = 01001000   
  x-1    = 01000111   
  x&(x-1)= 01000000   
  e2:   
  x      = 01001001   
  x-1    = 01001000   
  x&(x-1)= 01001000
可见只有前后x与x-1中的两个运算数都是 1 的时候结果才是1.
同理“|”运算也是两个运算数有一个为1就是1.
具体解释在代码中

AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,d,k;
int N[1000+5];
bool judge(int x)
{
    int c=0;
    while(x)
    {
        c++;              // 将x转化为2进制,看含有的1的个数。
        x&=(x-1);         //将最低的为1的位变成0
    }
    if(c<=k)              //限制个数
        return 1;
    else
        return 0;
}
int main()
{
    int s,t,total=0;
    scanf("%d%d%d",&n,&d,&k);
    for(int i=0; i<n; i++)
    {
        cin>>s;
        for(int j=0; j<s; j++)
        {
            cin>>t;
            N[i]|=1<<(t-1); //1<<t-1(1的二进制数整体向左移t-1位)
            //一起把二进制数的位数对应着来看,这两个数在这一位上有1的结果就是1,否则是0

        }
    }
    for(int i=0; i<(1<<d); i++)  //i<(1<<d)是当i不小于(1左移d位的数)时终止循环,枚举各子集对应的编码0,1,2,..2^d-1
    {
        if(judge(i))
        {
            int f=0;
            for(int j=0; j<n; j++)
            {
                if((N[j]|i)==i) f++;  //对应N[j]与i的并集与i相等,说明N[j]是它的子集
            }
            if(f>total)
                total=f;
        }
    }
    cout<<total<<endl;
    return 0;
}

 

 
 
 
 
 
 
posted @ 2015-07-29 15:48  hfcnal  阅读(325)  评论(0编辑  收藏  举报