HDU-6344
Problem Description
度度熊为了完成毕业论文,需要收集一些数据来支撑他的论据,于是设计了一份包含 m 个问题的调查问卷,每个问题只有 'A' 和 'B' 两种选项。
将问卷散发出去之后,度度熊收到了 n 份互不相同的问卷,在整理结果的时候,他发现可以只保留其中的一部分问题,使得这 n 份问卷仍然是互不相同的。这里认为两张问卷是不同的,当且仅当存在至少一个被保留的问题在这两份问卷中的回答不同。
现在度度熊想知道,存在多少个问题集合,使得这 n 份问卷在只保留这个集合的问题之后至少有 k 对问卷是不同的。
Input
第一行包含一个整数 T,表示有 T 组测试数据。
接下来依次描述 T 组测试数据。对于每组测试数据:
第一行包含三个整数 n,m 和 k,含义同题目描述。
接下来 n 行,每行包含一个长度为 m 的只包含 'A' 和 'B' 的字符串,表示这份问卷对每个问题的回答。
保证 1≤T≤100,1≤n≤103,1≤m≤10,1≤k≤106,给定的 n 份问卷互不相同。
Output
对于每组测试数据,输出一行信息 "Case #x: y"(不含引号),其中 x 表示这是第 x 组测试数据,y 表示满足条件的问题集合的个数,行末不要有多余空格。
Sample Input
2 2 2 1 AA BB 2 2 2 AA BB
Sample Output
Case #1: 3 Case #2: 0
状态压缩dp的应用
//
// main.cpp
// P1001
//
// Created by jinyu on 2018/8/9.
// Copyright © 2018年 jinyu. All rights reserved.
//
#include <iostream>
using namespace std;
const int MAXN = 1000+7; //问卷份数的最大值 1000
const int MAXS = 1024+7; //状态最大值为 2的10次方
char s[17];
int value[MAXN]; //value[i] 表示第i份问卷的值,A用 “1” 表示,B用 “0” 表示
int visit[MAXS]; //visit[i] 表示在当前问题集合的情况下,状态i出现的次数
int dp[MAXN][MAXS]; //dp[i][state]表示当前state状态在前i份问卷一共有对多少不同
int main(){
int T;
scanf("%d",&T);
int tt = T;
while(tt--){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i = 1;i<=n;i++){
value[i] = 0;
scanf("%s",s+1);
for(int j = 1;j<=m;j++){
if(s[j]=='A'){
value[i]+=(1<<(m-j));
}
}
}
printf("Case #%d: ",T-tt);
if(n*(n-1)/2 < k) printf("0\n"); //n取2的组合数一共有 n*(n-1)/2 种
else{
memset(dp, 0, sizeof(dp));
int ans = 0;
//state表示选中的问题集合,1则代表该问题被选中
//通过按位与&运算,state & value[j] 得到当前state问题集合下问题的答案
//按位与运算的应用:可以获取指定位的数值,如:x&100101 可以得到x的第1,4,6位的数值
//状态转移:dp[j][state] = dp[j-1][state] + (j-1) - visit[nowValue];
// dp[j][state]: 假定第j份和前面j-1份都不同,则可以产生j-1对不同的问卷,加上dp[j-1][state],然后去掉重复的,即减去visit[nowValue]
for(int state = 1;state<(1<<m);++state){ //问题集合至少选1个问题,至多选全部问题
memset(visit, 0, sizeof(visit));
for(int j = 1;j<=n;j++){
int nowValue = state & value[j]; //得到当前state问题集合下问题的答案
dp[j][state] = dp[j-1][state] + (j-1) - visit[nowValue];
visit[nowValue]++;
}
if(dp[n][state]>=k) ans++;
}
printf("%d\n",ans);
}
}
return 0;
}