HDOJ2806 位运算

Selecting Problems

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 861    Accepted Submission(s): 147


Problem Description
The college of computer & software is going to hold an ACM contest. N (0<N<=1000) students have registered for the contest, and the teacher LCY has thought up M (0<M<=15) problems numbered from 1 to M. LCY is so familiar with the students that he can forecast the result of every student for the M problems.
Now he will select as many problems as possible from the M problems. And he want at least K (0<=K<=M) students to solve all problems. Will you compute the maximum number of problems he can choose? Output "0" if there is no selection can satisfy the condition.
 

Input
First line of each case contains three integers: N, M and K. Then N lines follow, and each line has the format: 
Name P N1 N2 … NP.
Name (the length won't exceed 20), P (number of problems he/she can solve, 0<=P<=M), then P integers (represent the problems, 1-based).
 

Output
Output the maximum number of problems.
 

Sample Input
  
  
2 6 2 zl1 6 1 2 3 4 5 6 zl2 5 1 2 3 4 5 2 3 2 zl1 2 2 3 zl2 1 1
 

Sample Output
  
  
5 0
 

Source
 

Recommend
lcy   |   We have carefully selected several similar problems for you:   2807  2808  2801  2810  2809 

分析:每个同学可以A的题构成一个集合,预处理该集合,通过位运算高效的枚举该集合所有的子集。
同样的,所有题目构成一个集合,也是位运算枚举集合的每个子集。

令s为一个集合,枚举方法如下
for(int ss=s; ss; ss=(ss-1)&s) {...}

代码如下:
#include <cstdio>
#include <cstring>
using namespace std;
int num[1<<16];
int dp[1<<16];
int cnt[1<<16];
int n,m,k;

void init(){
   char s[25];
   int p;
   int ans,x;
   memset(dp,0,sizeof(dp));
   memset(cnt,0,sizeof(cnt));

   for (int i=1; i<=n; i++) {
       scanf("%s",s);
       scanf("%d",&p);
       ans = 0;
       for (int j=1; j<=p ;j++) {
           scanf("%d",&x);
           ans |= 1<<(x-1);
       }
       cnt[ans]++;
   }
}

inline int MAX(int x, int y){return x>y?x:y;}

void solve(){
    int ans = 0;
    for (int i=1<<m; i>=0; i--) if (cnt[i]) {
        for (int j=i; j; j=(j-1)&i) dp[j]+=cnt[i];
    }

    for (int i=0; i<1<<m; i++) if (dp[i]>=k) ans = MAX(ans,num[i]);
    printf("%d\n",ans);
}

int main(){
    memset(num,0,sizeof(num));
    for (int i=0; i<1<<16; i++) {
        for (int j=0; j<16; j++) if (i&(1<<j)) num[i]++;
    }

    while (scanf("%d %d %d",&n,&m,&k)!=EOF){
        init();
        solve();
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值