ch3 逻辑推理与枚举¶
1. 谁做的好事 —— 语义表示¶
- 方法一:用字符来表示,
'A'代表 A,'B'代表 B,\(...\) - 方法二:给 A、B、C、D 编号,\(0\) 号是 A,\(1\) 号是 B,\(...\)
两种方法本质是一样的,在 C++ 中,字符本质上是整数(\(\tt ASCII\) 码)。
变量不仅能用于表示代数计算中的数量,还能够用于表示特定概念的事物,因此我们可以定义一个变量表示 “做好事的人”。
赋值语句表示的是一个既成事实,或者在当前情况下认定的事实,是一种 “真理”。问题中每个人的话实际上只是一种说法,一种论断,需要判断 “真伪”。
!= 和 == 称为关系运算符,分别表示 “不等于” 和 “等于”。含有关系运算符的式子称为关系表达式,关系表达式的计算结果是布尔类型。关系运算符还有大于 >、大于等于 >=、小于 <、小于等于 <=。
2. 谁做的好事 —— 真假检查¶
条件语句 if 可以使得部分程序语句在满足一定条件的情况下才能执行。


C++
int count = 0; // 说真话的人数
if (good_man != 'A') // A 说了真话
count++;
if (good_man == 'C') // B 说了真话
count++;
if (good_man == 'D') // C 说了真话
count++;
if (good_man != 'D') // D 说了真话
count++;
if (count == 3) // 有 3 个人说了真话
cout << good_man << endl; // 输出做好事的人
3. 谁做的好事 —— 循环枚举¶
循环语句是让程序循环执行一些代码指令的语句。

C++
for (good_man = 'A'; good_man <= 'D'; good_man++) {
int count = 0; // 说真话的人数
if (good_man != 'A') // A 说了真话
count++;
if (good_man == 'C') // B 说了真话
count++;
if (good_man == 'D') // C 说了真话
count++;
if (good_man != 'D') // D 说了真话
count++;
if (count == 3) { // 有 3 个人说了真话
cout << good_man << endl; // 输出做好事的人
break; // 跳转语句,找到答案之后提前结束循环
}
}
枚举往往通过循环实现,通过合理巧妙设计 \(3\) 个表达式,依次不重复不遗漏的枚举所有可能答案,并进行检测。
4. 谁是嫌疑犯 —— 多重循环¶
把事物进行人为有序的编号,利用编号可以进行更方便的计算。比如用 \(0\) 表示没有参与作案,用 \(1\) 表示参与作案:
C++
int A, B, C, D, E, F;
for (A = 0; A <= 1; A++)
for (B = 0; B <= 1; B++)
for (C = 0; C <= 1; C++)
for (D = 0; D <= 1; D++)
for (E = 0; E <= 1; E++)
for (F = 0; F <= 1; F++)
// 检测答案是否正确并输出
5. 谁是嫌疑犯 —— 破案线索表示¶
-
逻辑与
&&,表达同时成立的含义
-
逻辑或
||,表达至少一个成立的含义
-
逻辑非
!,表达不成立的含义
C++
int A, B, C, D, E, F;
bool found = false; // break 只能退出一层循环,用标志变量退出多层循环更简单
for (A = 0; A <= 1 && !found; A++)
for (B = 0; B <= 1 && !found; B++)
for (C = 0; C <= 1 && !found; C++)
for (D = 0; D <= 1 && !found; D++)
for (E = 0; E <= 1 && !found; E++)
for (F = 0; F <= 1 && !found; F++) {
bool b1 = (A == 1) || (B == 1);
bool b2 = ((A == 1) && (E == 1)) ||
((A == 1) && (F == 1)) ||
((E == 1) && (F == 1));
bool b3 = !((A == 1) && (D == 1));
bool b4 = ((B == 1) && (C == 1)) ||
((B == 0) && (C == 0));
bool b5 = ((C == 1) && (D == 0)) ||
((C == 0) && (D == 1));
bool b6 = ((D == 0) && (E == 0)) || (D == 1);
if (b1 && b2 && b3 && b4 && b5 && b6) {
cout << A << B << C << D << E << F << endl;
found = true;
}
}
6. 谁是嫌疑犯 —— 用二进制枚举¶
如果用 \(1\) 个二进制位来表示一个人是否作案,于是 \(6\) 个人是否作案的情况可以表示成一个 \(6\) 位二进制数,因此我们可以用一层循环来枚举所有的 \(6\) 位二进制数即可。
获取一个数的二进制位需要用到位运算。

C++
for (int i = 0; i < (1 << 6); i++) {
int A = (i >> 5) & 1;
int B = (i >> 4) & 1;
int C = (i >> 3) & 1;
int D = (i >> 2) & 1;
int E = (i >> 1) & 1;
int F = i & 1;
bool b1 = (A == 1) || (B == 1);
bool b2 = ((A == 1) && (E == 1)) ||
((A == 1) && (F == 1)) || ((E == 1) && (F == 1));
bool b3 = !((A == 1) && (D == 1));
bool b4 = ((B == 1) && (C == 1)) || ((B == 0) && (C == 0));
bool b5 = ((C == 1) && (D == 0)) || ((C == 0) && (D == 1));
bool b6 = ((D == 0) && (E == 0)) || (D == 1);
if (b1 && b2 && b3 && b4 && b5 && b6) {
cout << A << B << C << D << E << F << endl;
break;
}
}