AtCoder Beginner Contest 356 C 题
问题陈述
您有 把钥匙,编号为 。 其中一些是真钥匙,而其他的是假钥匙。
有一扇门,门 X,您可以将任意数量的钥匙插入其中。当且仅当至少插入 把真钥匙时,门 X 才会打开。
您已对这些钥匙进行了 次测试。第 \ 次测试如下:
- 您将 把钥匙 插入门 X。
- 测试结果用一个英文字母 表示。
-
o表示门 X 在第 \ 次测试中打开。 -
x表示门 X 在第 次测试中没有打开。
有 个可能的组合,其中哪些钥匙是真的,哪些是假的。在这些组合中,找出与任何测试结果都不矛盾的组合数。 给定的测试结果可能不正确,并且没有组合满足条件。在这种情况下,报告 。
约束
- 、 、 、 和 为整数。
- 如果 ,则为 。
- 为
o或x。
思路分析:
在 1~N把钥匙中有些是假的,有些是真的,真假我们不知道,每种钥匙都有两种可能的情况,0为假,1为真。
我们不是需要找到钥匙那些是真,那些是假,不要被题意给误导,我们需要找到是测试的结果是否与条件是否相矛盾,不矛盾则为真,矛盾则为假。 例如 :
- 当真钥匙的数量小于 K 时,门可以被打开,则此组合是错误的。
- 当真钥匙的数量大于或等于 K 时,门可以被打开,则此组合是正确的。
- 当门没有被打开时,当真钥匙的数量大于 K 时,则此组合是错误的
- 当门没有被打开时,当真钥匙的数量小于 K 时,则此组合是错误的 此题用位运算会比较快。
#include<iostream>
const int N = 110;
int f[N];
char r[N];
void solve() {
int n, m, k ;
std::cin >> n >> m >> k;
for(int i = 0; i < m; i++) {
int t, x;
std::cin >> t;
while(t--) {
std::cin >> x;
x--;
f[i] |= 1 << x; // 此代码,是第x把钥匙被测试
}
std::cin >> r[i];
}
int res = 0;
for(int i = 0; i < 1 << n; i++) { // i < 1<<n ,二进制表示集合,表示有n把钥匙的可能的情况,用十进制的方式表示
int ok = 1;
for(int j = 0; j < m; j++) {
// 假设 i 的情况是正确的
// __builtin_popcount(i & f[j]) 枚举出正确的钥匙.
bool t = __builtin_popcount(i & f[j]) >= k;
// 当 门被打开时,钥匙的数量小于 k 则为false ,
// 当门 没有被打开,钥匙的数量大于 k ,则为false.
if(r[j] == 'o' && !t || r[j] == 'x' && t) ok = 0;
}
res += ok;
}
std::cout << res << '\n';
}
int main() {
int T = 1;
while(T--) {
solve();
}
return 0;
}