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

文章目录

  • [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

上表中的 truefalsebool(布尔)型常量,这是一种用于表达 "真" 和 "假" 的数据类型,其中 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。

本节习题

相关推荐
逆境清醒2 小时前
Python中的常量
开发语言·python·青少年编程
yongui478342 小时前
异步电机最小二乘法参数辨识的MATLAB实现
算法·matlab·最小二乘法
汉克老师2 小时前
GESP2025年9月认证C++二级真题与解析(编程题2(菱形))
c++·找规律·二维数组·枚举算法·曼哈顿距离·模拟画图
君义_noip2 小时前
信息学奥赛一本通 1528:【例 2】单词游戏
c++·算法·信息学奥赛·一本通·csp-s
AlenTech2 小时前
238. 除了自身以外数组的乘积 - 力扣(LeetCode)
算法·leetcode·职场和发展
l1t2 小时前
利用豆包辅助编写数独隐式唯一数填充c程序
c语言·开发语言·人工智能·算法·豆包·deepseek
FL16238631292 小时前
[C++][cmake]基于C++在windows上部署yolo26的目标检测onnx模型
c++·windows·目标检测
cici158742 小时前
matlab实现NSGA-II(带精英策略的非支配排序遗传算法)
算法
乐迪信息2 小时前
乐迪信息:智能识别船舶种类的AI解决方案
大数据·网络·人工智能·算法·无人机