赛题如下
Problem Description
度度熊为了完成毕业论文,需要收集一些数据来支撑他的论据,于是设计了一份包含 m 个问题的调查问卷,每个问题只有 'A' 和 'B' 两种选项。将问卷散发出去之后,度度熊收到了 n 份互不相同的问卷,在整理结果的时候,他发现可以只保留其中的一部分问题,使得这 n 份问卷仍然是互不相同的。这里认为两张问卷是不同的,当且仅当存在至少一个被保留的问题在这两份问卷中的回答不同。现在度度熊想知道,存在多少个问题集合,使得这 n 份问卷在只保留这个集合的问题之后至少有 k 对问卷是不同的。
Input
第一行包含一个整数 T,表示有 T 组测试数据。接下来依次描述 TTT 组测试数据。对于每组测试数据:第一行包含三个整数 n,m 和 k,含义同题目描述。接下来 n 行,每行包含一个长度为 m 的只包含 'A' 和 'B' 的字符串,表示这份问卷对每个问题的回答。保证 1≤T≤100 ,1≤n≤10^3, 1≤m≤10,1≤k≤10^6,给定的 n 份问卷互不相同。
Output
对于每组测试数据,输出一行信息 "Case #x: y"(不含引号),其中 x 表示这是第 x 组测试数据,y 表示满足条件的问题集合的个数,行末不要有多余空格。
Sample Input
Copy
2
2 2 1
AA
BB
2 2 2
AA
BB
Sample Output
Copy
Case #1: 3
Case #2: 0
解题思路:
本题需要找到一个多少个问题集合满足条件,由于这些问题集合实际相当于总体问题集合子集,所以再2^m个子集中找满足要求的解。再其次就是判断你找到的子集是否满足条件,从最直观条件来看就是将所有问卷调查用两两取出判断相等与否。看不想等的次数是否大于K.(两两不放回取出的数量是n*(n-1)/2)这样的话时间复杂度是O(2^m*n^2)级别的。这样的复杂度会超时。所以像是否有别的思路。
另一种思路:我们将A-B的问题序列转化成为0-1二进制序列从而转为整数特征,而问题子集情况也可以使用0-1进制序列表示。通过位运算就可以表示问题子集对应的问题结果0-1序列,进而转为整数特征。先将n个问卷的问题特征值排序。排序后就可以清晰得到相同问题问卷数目,比如有这样的排序结果[1 2 2 3 3],那么问卷2 和 3的问题是一样的,问卷 4 和 5 的是一样的。通过相同问题数目就可以很容易得到相同问题问卷的对数。我们想如果将总的问题对数减去相同的问题的问卷对数那么就得到了不一样问卷的对数。上面的例子就是5 * 4 / 2 - 2 * 1 / 2 - 2 * 1 / 2 等于8。
好了直接上代码。
#include <bits/stdc++.h>
using namespace std;
int T,n,m,k;
int answers[1010];
int process_answers[1010];
void replace(string &s){
for (int i=0; i<m; i++){
if (s[i] == 'A') {
s[i] = '1';
} else if (s[i] == 'B') {
s[i] = '0';
}
}
}
bool judge(int save_flag) {
// 对于原问题进行保留处理
for (int i=0; i<n; i++) {
process_answers[i] = answers[i] & save_flag;
}
// 统计每一个问题结果出现次数
map<int,int> count_fre;
for (int i=0; i<n; i++) {
if (count_fre.count(process_answers[i]) > 0) {
count_fre[process_answers[i]] += 1;
} else {
count_fre[process_answers[i]] = 1;
}
}
// 利用总对数减去相同问题结果对数
int total = n * (n - 1) / 2;
for (map<int, int>::iterator iter = count_fre.begin(); iter != count_fre.end(); iter++) {
int cnt = iter->second;
if (cnt >= 2) {
total -= (cnt * (cnt - 1) / 2);
}
}
if (total >= k) {
return true;
} else {
return false;
}
}
void print(int casei, int res){
printf("Case #%d: %d\n",casei, res);
}
int main() {
scanf("%d",&T);
for (int casei = 1; casei<=T; casei++) {
scanf("%d%d%d",&n,&m,&k);
for (int i=0; i<n; i++) {
string s;
cin >> s;
replace(s);
bitset<11> bs(s);
answers[i] = bs.to_ulong();
}
int l = 1, r = (1 << m) - 1, res = 0;
for (int i = l; i <= r; i++) {
if (judge(i)) {
res += 1;
}
}
print(casei,res);
}
return 0;
}