第二章 分支结构程序设计(3)

文章目录

  • [2.4 关系的逻辑运算](#2.4 关系的逻辑运算)
  • [2.5 分支结构应用](#2.5 分支结构应用)

2.4 关系的逻辑运算

前面的例题中有一类范围检测问题,例如要判断 x x x 是否介于 a a a 和 b b b 之间( a ≤ b a≤b a≤b),即检测 a ≤ x ≤ b a≤x≤b a≤x≤b 是否成立。需要注意的是,在程序中不能直接使用 if 语句检测 a ≤ x ≤ b a≤x≤b a≤x≤b 的值,因为程序会先计算 a ≤ x a≤x a≤x 的值,然后用这个值再与 b b b 进行比较,也就是说 a ≤ x ≤ b a≤x≤b a≤x≤b 在程序中等价于 ( a ≤ x ) ≤ b (a≤x)≤b (a≤x)≤b。我们知道 a ≤ x a≤x a≤x 的值是 bool 型,只有 0 和 1 两种取值,如果 b b b 是一个正整数,那么 ( a ≤ x ) ≤ b (a≤x)≤b (a≤x)≤b 在程序中将永远为 true

在程序中要想正确表达范围检测的逻辑,应该检测 a ≤ x a≤x a≤x 与 x ≤ b x≤b x≤b 是否同时成立。虽然在多分支结构中我们利用 else 子句的特性省去了部分条件的检测,但是并非所有问题都可以利用 else 子句进行简化。因此我们需要学习一种新的运算 ------ 逻辑运算,运算符号与运算规则如下表。

逻辑运算符 逻辑表达式 逻辑表达式的值
&&(与) A && B 如果子表达式 A 和 B 的值都为 true,则结果为 true,否则为 false;如果 A 为 false,则不计算子表达式 B,称为短路
` `(或)
!(非) !A 如果子表达式 A 为 true,则结果为 false,否则为 true

根据上面的规则,判断 x x x 是否介于 a a a 和 b b b 之间( a ≤ b a≤b a≤b)的 C++ 表达式应该是 a <= x && x <= b。由于关系运算优先级高于逻辑与,因此这里并不需要添加括号来提升优先级。

此外,C++ 还允许使用关键字 andornot 来表达逻辑运算符 &&||!,不过并不是很常用,建议大家采用符号表示。

【小贴士】目前所学的运算符优先级如下(从高到低,同一括号内优先级相同):(逻辑非),(乘、除、取余),(加、减),(大于、大于等于、小于、小于等于),(等于、不等于),(逻辑与),(逻辑或),(赋值、复合赋值)。

【D1067 [21 年 6 月一级] 大写字母的判断】输入一个字符,判断是否是英文大写字母,即是否是 'A' ~ 'Z' 中的一个。如果是英文大写字母,则输出 YES,否者输出 NO

前面我们了解到字符型数据是可以直接参与关系运算的,并且由于大写英文字母在 ASCII 码表中是连续存储的,结合逻辑运算的规则,解决这个问题的关键代码如下。

cpp 复制代码
if ('A' <= ch && ch <= 'Z') {
    cout << "YES\n";
}
else {
    cout << "NO\n";
}

【P2819 [ABC208A] Rolling Dice】小高有一个六面骰子,每面分别标有 1 到 6 的数字。他想知道投掷 a a a 次骰子,是否有可能得到总和为 b b b 的点数。如果能够得到总和为 b b b 的点数,输出 Yes;否则,输出 No

每个骰子最小点数是 1,最大点数是 6,于是投掷 a a a 次可以得到的最小总和为 a a a,最大总和为 6 a 6a 6a,只需要检测 b b b 是否介于 a a a 到 6 a 6a 6a 之间即可。

cpp 复制代码
if (a <= b && b <= 6 * a) {
    cout << "Yes\n";
}
else {
    cout << "No\n";
}

【P1539 闰年判断】输入一个年份(大于 1582 的整数),判断这一年是否是闰年,如果是输出 1,否则输出 0。

闰年分为两种情况:整世纪年须是 400 的倍数;非整世纪年须是 4 的倍数。两种情况满足任意一种即可,注意第二种情况需要判断这一年不是整世纪年。

cpp 复制代码
if (y % 400 == 0 || y % 100 != 0 && y % 4 == 0) {
    cout << "1\n";
}
else {
    cout << "0\n";
}

由于逻辑与(&&)的优先级高于逻辑或(||),后面两个条件可以不用添加括号。

【P2821 [ABC223A] Exact Price】高桥的钱包里有一个或多个 100 元硬币,没有其他东西。请问钱包里的总金额是否可能是 x x x 元?如果高桥钱包里的总金额可能是 x x x 元,请输出 Yes;否则,输出 No, 0 ≤ X ≤ 1000 0≤X≤1000 0≤X≤1000。

这里只需要检测 x x x 是否为 100 的倍数即可,注意 x x x 有可能为 0,而钱包里的硬币至少有 1 个,所以 x x x 等于 0 是不可能的。

cpp 复制代码
if (!x || x % 100 != 0) {  // !x 等价于 x == 0
    cout << "No\n";
}
else {
    cout << "Yes\n";
}

本节习题

2.5 分支结构应用

与顺序结构类似,接下来通过几个稍微复杂的问题,进一步提升我们的编程能力与解决问题能力。

【D1002 [OpenJudge] 有一门课不及格的学生】给出一名学生的语文和数学成绩,判断他是否恰好有一门课不及格(成绩小于 60 分)。若该生恰好有一门课不及格,输出 1;否则输出 0。

可以定义一个变量 cnt,用于统计不及格科目的数量,最后检测 cnt 的值是否为 1 即可。需要注意的是变量 cnt 需要初始化为 0。

cpp 复制代码
int cnt = 0;
if (a < 60) {
    cnt = cnt + 1;
}
if (b < 60) {
    cnt = cnt + 1;
}

cout << (cnt == 1 ? "1\n" : "0\n");

【D1168 [22 年 6 月一级] 最小的数】依次输入 3 个整数 a a a、 b b b、 c c c,将 a a a、 b b b、 c c c 中的最小值输出。

不妨假设 a 就是最小值,然后分别检测 bc 是否小于 a,在小于 a 的情况下用赋值语句更新 a 的值即可,上述思想方法称为 "打擂台"。

cpp 复制代码
if (b < a) a = b;
if (c < a) a = c;

cout << a;

【D1196 [22 年 9 月一级] 简单排序】依次输入 3 个整数 a a a、 b b b、 c c c,将他们从小到大的顺序输出。

利用打擂台的方法可以找到最小值,如果最初 a 并不是最小值,则会导致原来 a 中的数据丢失,后续步骤将出现错误。为了避免这种情况的出现,我们将打擂台过程中的赋值语句修改为交换操作。这样在经过两个单分支语句之后,变量 a 中就存储了最小值,而 bc 中就是另外两个数值,在 c 小于 b 的情况下只需要交换它们的值即可实现 a < b < c a < b < c a<b<c。

cpp 复制代码
if (b < a) {
    t = a, a = b, b = t;  // 利用逗号运算符简化代码结构
}
if (c < a) {
    t = a, a = c, c = t;
}
if (c < b) {
    t = b, b = c, c = t;
}

cout << a << " " << b << " " << c << endl;

【P6031 [COCI 2007/2008 #3] CETIRI】你原本有 4 个数,它们从小到大排序后构成了等差数列。但是现在丢失了一个数,并且其余的三个数的顺序也被打乱了。请你找出第四个数。如果结果不唯一,输出任意一个即可。

按照从小到大排序之后,假设前两项的差为 x x x,后两项的差为 y y y,

  • 如果 x x x 大于 y y y,则说明缺失第 2 项,且 y y y 是公差;
  • 如果 x x x 小于 y y y,则说明缺失第 3 项,且 x x x 是公差;
  • 如果 x x x 等于 y y y,则说明缺失第 1 项或第 4 项。
cpp 复制代码
int x = b - a, y = c - b;
if (x > y) {
    cout << a + y << endl;
}
else if (x < y) {
    cout << b + x << endl;
}
else {
    cout << c + x << endl;  // 输出a - x也是正确的
}

【D1227 [OpenJudge] 鸡兔同笼】一个笼子里面关了鸡和兔子(鸡有 2 只脚,兔子有 4 只脚,没有例外)。已经知道了笼子里面脚的总数 a a a,问笼子里面至少有多少只动物,至多有多少只动物。如果没有满足要求的答案,则输出两个 0,中间用一个空格分开。

由于鸡和兔子的脚都是偶数,如果 a a a 为奇数,则没有满足要求的答案;否则最少的动物数量应满足兔子数量最多,此时答案为 ⌈ a / 4 ⌉ ⌈a/4⌉ ⌈a/4⌉;最多的动物数量应满足鸡的数量最多,此时答案为 a / 2 a/2 a/2。

cpp 复制代码
if (a % 2) 
    printf("0 0\n");
else 
    printf("%d %d\n", (a + 3) / 4, a / 2);

【P2822 [ABC228A] On and Off】小高每天在 s s s 点打开房间的灯,在 t t t 点关闭房间的灯(使用 24 小时制)。灯亮着的时候可能会跨越两天。请判断在 x x x 点 30 分时灯是否是亮着的, s ≠ t s≠t s=t。

一个比较容易想到的方法是根据 s s s 与 t t t 的大小关系进行分类讨论,

  • 如果 s < t s < t s<t,说明开灯和关灯是在同一天,那么当 s ≤ x < t s \le x < t s≤x<t 时,灯是开着的;
  • 如果 s > t s > t s>t,说明开灯和关灯跨越到了第二天,那么当 t ≤ x < s t\le x < s t≤x<s 时,灯是关着的。

虽然上面的方法比较清晰易懂,但是代码较为复杂,留给读者自行实现,下面介绍一种计算时差的方法。先用 t = t − s t=t-s t=t−s 计算出每天开灯的时长 t t t,用 x = x − s x=x-s x=x−s 计算出目标时间点距离开灯时间点的时间差 x x x。由于时间可能会跨越两天,因此两个时间差都有可能是负数,可以给时间差加上 24 之后再对 24 取余来保证时间差都是非负的。接下来只需要检测目标时间点距离开灯时间点的时间差 x x x 是否小于开灯时长 t t t 即可。

cpp 复制代码
t = (t - s + 24) % 24;
x = (x - s + 24) % 24;

if (x < t) {
    cout << "Yes\n";
}
else {
    cout << "No\n";
}

本节习题

相关推荐
想逃离铁厂的老铁2 小时前
Day42 >> 188、买卖股票的最佳时机IV + 309.最佳买卖股票时机含冷冻期 + 714.买卖股票的最佳时机含手续费
算法·leetcode·职场和发展
uoKent2 小时前
构造析构综合练习
c++
wu_asia2 小时前
方阵对角线元素乘积计算
数据结构·算法
想逃离铁厂的老铁2 小时前
Day43 >> 300.最长递增子序列 + 674. 最长连续递增序列+ 718. 最长重复子数组
数据结构·算法
Yzzz-F2 小时前
P6648 [CCC 2019] Triangle: The Data Structure [st表]
算法
FL16238631292 小时前
[C++][cmake]基于C++在windows上onnxruntime+opencv部署yolo26-seg的实例分割onnx模型
c++·windows·opencv
LateFrames2 小时前
泰勒级数:从 “单点” 到 “理论与实践的鸿沟”
学习·算法
武帝为此3 小时前
【RC4加密算法介绍】
网络·python·算法
宵时待雨3 小时前
数据结构(初阶)笔记归纳4:单链表的实现
c语言·开发语言·数据结构·笔记·算法