文章目录
- [2.1 数据的关系运算](#2.1 数据的关系运算)
- [2.2 单分支与双分支结构](#2.2 单分支与双分支结构)
让我们回到机器人服务员那个楔子,当机器人走到厨房时,它需要找一个干净的杯子。假设它已经知道了什么样的杯子是干净的,那么它会在厨房拿起一个杯子进行检测,如果这个杯子是干净的,就执行下一步;否则它应该放下这个杯子,寻找另一个干净的杯子。
上述检测杯子是否干净的动作是一种选择行为,在程序中用选择结构来表达这种逻辑,也称为分支结构。事实上,生活中处处存在选择行为,比如周末你的父母会根据天气情况选择娱乐活动,去餐馆吃饭会根据个人喜好和餐品好吃程度选择餐馆,体育老师会根据班主任的心情决定是否"生病"。
2.1 数据的关系运算
在开始学习选择结构之前,我们需要先掌握 C++ 中做比较的规则。机器人应该要知道如何判断杯子是否干净,事实上机器人并不能直接做出判断,它会将 "干净" 作为一种状态存储起来,然后拿起一个杯子检测其状态是否为 "干净",这就涉及到在程序中如何确定两个状态值相同。扩展到数值类型上,就会涉及到如何确定数值 A 与数值 B 的关系。这里需要用到关系运算符,符号表示和含义如下表。
| 关系运算符 | 关系表达式 | 关系表达式的值 |
|---|---|---|
< |
A < B |
如果数值 A 小于 B,则表达式的值为 true,否则为 false |
<= |
A <= B |
如果数值 A 小于等于 B,则表达式的值为 true,否则为 false |
> |
A > B |
如果数值 A 大于 B,则表达式的值为 true,否则为 false |
>= |
A >= B |
如果数值 A 大于等于 B,则表达式的值为 true,否则为 false |
== |
A == B |
如果数值 A 等于 B,则表达式的值为 true,否则为 false |
!= |
A != B |
如果数值 A 不等于 B,则表达式的值为 true,否则为 false |
上表中的 true 和 false 是 bool(布尔)型常量,这是一种用于表达 "真" 和 "假" 的数据类型,其中 true 表示真,false 表示假。bool 型变量在内存中占用 1 个字节,事实上程序中存储 true 时会用 1 替代,存储 false 时会用 0 替代。可以使用 cout << true; 和 cout << false; 来验证。
【小贴士】C++ 中任意合法表达式的值都可以转换为 bool 值,比如变量、常量、算术表达式。如果表达式的值为 0,则转换为 bool 值就是 false;否则转换为 bool 值就是 true。
2.2 单分支与双分支结构
在掌握了 C++ 中的关系运算之后,我们通过解决几个实际问题来学习分支结构。
【D1040 [OpenJudge] 苹果和虫子 2】你买了一箱 n n n 个苹果,很不幸的是买完时箱子里混进了一条虫子。虫子每 x x x 小时能吃掉一个苹果,假设虫子在吃完一个苹果之前不会吃另一个,那么经过 y y y 小时你还有多少个完整的苹果?
此题与【G1181 图书管理的老鼠】几乎一样,唯一的区别在于此题并不保证 y y y 小时后至少剩下一个完整的苹果。因此表达式 t = n - (y + x - 1) / x 的值可能小于 0,此时我们需要将t的值修正为 0。
我们可以使用 if 语句检测 t < 0 t<0 t<0 是否成立,当 t < 0 t<0 t<0 成立时,通过赋值语句将t的值修改为 0 即可。解决这个问题的完整代码如下,注意 if 语句的语法格式。
cpp
#include <iostream>
using namespace std;
int main()
{
int n, x, y;
cin >> n >> x >> y;
int t = n - (y + x - 1) / x;
if (t < 0) {
t = 0;
}
cout << t << endl;
return 0;
}
像这种在某种条件成立的情况下,执行特定代码的语句结构称为单分支结构。关键字 if 后面圆括号中的关系表达式是检测条件,之后花括号中的语句是条件成立时会执行的代码,花括号中可以有多条语句。为了保持代码的可读性,花括号中的语句应缩进 2 ∼ 4 2\sim 4 2∼4 个位置。
【小贴士】在 C++ 语言中,花括号的作用是将多条语句当做一个整体,称为复合语句。事实上,if 语句在条件成立时只允许执行一条语句,多数时候这并不能满足我们的需求,于是引入复合语句,分支结构也因此有了更丰富的变化。
【G1001 [GESP 样题一级] 时间转换】小明刚刚学习了小时、分和秒的换算关系。他想知道一个给定的时刻是这一天的第几秒,你能编写一个程序帮帮他吗?输入一行,包含三个整数和一个字符。三个整数分别表示时刻的时、分、秒;字符有两种取值,大写字母
'A'表示上午,大写字母'P'表示下午。
我们可以很轻松的计算出给定时刻的秒数 t = h × 3600 + m × 60 + s t=h×3600+m×60+s t=h×3600+m×60+s,如果是下午的时刻,则 t t t 是从 12:00:00 开始计算的。想要得到从 00:00:00 开始计算的秒数,只需要给 t t t 加上 12 × 3600 12×3600 12×3600 即可。换言之,我们需要在程序中检测给定字符 ch 与 'P' 是否相等。
cpp
#include <iostream>
using namespace std;
int main()
{
int h, m, s;
char ch;
cin >> h >> m >> s >> ch;
int t = h * 3600 + m * 60 + s;
if (ch == 'P') {
t += 12 * 3600;
}
cout << t << endl;
return 0;
}
【D1195 [22年9月一级] 成绩判定】给定一门课的考试分数,如果分数大于等于 60,则该门课成绩合格,记为
P,否者成绩不合格,记为F。
显然可以用两个 if 语句解决此问题,不过无论我们如何组织代码顺序,总有一种情况需要进行两次检测才能得到结果。这里我们学习一种新的语句 if - else,称为双分支结构,它可以在只检测一次的情况下得出两种截然相反的结果,代码如下。
cpp
#include <iostream>
using namespace std;
int main()
{
int s;
cin >> s;
if (s >= 60) {
cout << "P\n";
}
else { // 表示关系表达式s >= 60的值为false的情况,即s < 60
cout << "F\n";
}
return 0;
}
代码中 else 子句所代表的含义是 if 子句检测的条件不成立的情况,也就是说 else 子句一定出现在 if 子句之后,不能单独出现。
【P2192 [ABC102A] 倍数】给定正整数 n n n。请求 2 和 n n n 都能整除的最小正整数。
不难发现,如果 n n n 是偶数,那么满足条件的最小正整数就是 n n n;如果 n n n 是奇数,满足条件的最小正整数是 2 n 2n 2n。可以通过检测表达式 n % 2 == 0 是否成立来判断 n n n 是否为偶数。进一步地,可以通过检测表达式 a % b == 0 是否成立来判断 a 是否为 b 的倍数。
cpp
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
if (n % 2 == 0) { // 表示表达式 n % 2 == 0 的值为 true,即 n 是偶数
cout << n << endl;
}
else { // 表示表达式 n % 2 == 0 的值为 false,即 n 是奇数
cout << 2 * n << endl;
}
return 0;
}
【G1229 [GESP2509 一级] 商店折扣】商店正在开展促销活动,给出了两种方案的折扣优惠。第一种方案是购物满 x x x 元减 y y y 元;第二种方案是直接打 n n n 折,也就是说价格变为原先的 n / 10 n/10 n/10。这里的 x , y , n x,y,n x,y,n 均是正整数,并且 1 ≤ y < x 1≤y<x 1≤y<x, 1 ≤ n < 10 1≤n<10 1≤n<10。需要注意的是,第一种方案中满减优惠只能使用一次。例如购物满 10 元减 3 元时,若挑选了价格总和为 33 元的物品,只能减免 3 元,需要支付 30 元。小明在商店挑选了价格总和为 p p p 元的物品,结账时只能使用一种优惠方案。小明最少需要支付多少钱呢?保留两位小数。
首先需要分别计算出两种方案支付的钱数,然后判断哪种方案支付的钱数更少。对于满减方案,还需要检测是否满足满减条件,因此这里需要用到两个并列的分支结构。第一个分支结构用于计算方案一支付的钱数,第二个分支结构用于判断哪种方案更省钱。
需要注意的是两种方案费用的比较涉及到浮点数。浮点数在计算机中并不能保证精确存储,往往存在一个较小的误差,例如 0.1 可能存储为 0.099999... 或者 0.1000...1。因此在比较浮点数时建议根据题目所要求的精度,设置一个误差限来进行比较,以此避免浮点数的不精确存储导致的比较错误,具体参考下面的代码。
cpp
#include <iostream>
#include <cstdio>
using namespace std;
const double eps = 1e-6;
int main()
{
int x, y, n, p;
cin >> x >> y >> n >> p;
double s1, s2;
if (p >= x) { // 表示满足满减的条件,即 p >= x
s1 = p - y;
}
else { // 表示不满足满减的条件,即 p < x
s1 = p;
}
s2 = p * n / 10.0;
if (s1 + eps < s2) { // 表示方案一更优惠
printf("%.2f\n", s1);
}
else { // 表示方案二更优惠
printf("%.2f\n", s2);
}
return 0;
}
C++ 提供了一种三目运算符,用于表达简单的双分支结构,让代码结构更加紧凑,这是一种便于我们 "偷懒" 的语法规则,此类规则统称为 "语法糖"。建议在代码中尽量少的使用此类语法规则,因为这会大大降低代码可读性。
【小贴士】所谓三目运算符,是指需要 3 个操作数参与的运算符。同理,需要两个操作数的运算符可以称为双目运算符,比如算术运算符、关系运算符;只需要一个操作数的运算符称为单目运算符,例如正负号。
语法格式为 A ? B : C,整个表达式称为条件表达式,含义是若子表达式 A 的值为 true,则条件表达式的值为子表达式 B 的值,否则为 C 的值。于是我们可以改写上面的程序。
cpp
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int x, y, n, p;
cin >> x >> y >> n >> p;
double s1, s2;
s1 = (p >= x ? p - y : p);
s2 = p * n / 10.0;
printf("%.2f\n", s1 < s2 ? s1 : s2);
return 0;
}
此外,C++ 还提供了一种逗号运算符,用于连接表达式,简化代码结构,使代码更加紧凑。这与后面即将学到的复合语句的功能类似,但是侧重点不同。而且逗号运算符只适用于比较简单的情况,如果在复杂情况下使用将会大大降低代码的可读性。
逗号运算符不限制子表达式的个数,构成的式子称为逗号表达式,其值是最后一个子表达式的值。例如 cout << (a = 5, b = 7, c = -1); 的输出结果是 − 1 -1 −1;再例如表达式 a, b = 3, 5 将给变量 b 赋值为3,a 没有赋值,5 没有作用,而整个表达式的值是 5。