欢迎光临
我们一直在努力

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

文章目录

  • 2.4 关系的逻辑运算
    • 本节习题
  • 2.5 分支结构应用
    • 本节习题

2.4 关系的逻辑运算

前面的例题中有一类范围检测问题,例如要判断

x

x

x 是否介于

a

a

a

b

b

b 之间(

a

b

a≤b

ab),即检测

a

x

b

a≤x≤b

axb 是否成立。需要注意的是,在程序中不能直接使用 if 语句检测

a

x

b

a≤x≤b

axb 的值,因为程序会先计算

a

x

a≤x

ax 的值,然后用这个值再与

b

b

b 进行比较,也就是说

a

x

b

a≤x≤b

axb 在程序中等价于

(

a

x

)

b

(a≤x)≤b

(ax)b。我们知道

a

x

a≤x

ax 的值是 bool 型,只有 0 和 1 两种取值,如果

b

b

b 是一个正整数,那么

(

a

x

)

b

(a≤x)≤b

(ax)b 在程序中将永远为 true。

在程序中要想正确表达范围检测的逻辑,应该检测

a

x

a≤x

ax

x

b

x≤b

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

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

根据上面的规则,判断

x

x

x 是否介于

a

a

a

b

b

b 之间(

a

b

a≤b

ab)的 C++ 表达式应该是 a <= x && x <= b。由于关系运算优先级高于逻辑与,因此这里并不需要添加括号来提升优先级。

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

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

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

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

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 之间即可。

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

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

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

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

0X1000

这里只需要检测

x

x

x 是否为 100 的倍数即可,注意

x

x

x 有可能为 0,而钱包里的硬币至少有 1 个,所以

x

x

x 等于 0 是不可能的。

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

本节习题

  • D1093 [21 年 9 月一级] 数字判断
  • D1221 [22 年 12 月一级] 判断是否存在重复的数
  • P2817 [ABC204A] Rock-paper-scissors
  • G1185 [GESP2503 二级] 时间跨越

2.5 分支结构应用

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

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

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

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 就是最小值,然后分别检测 b 和 c 是否小于 a,在小于 a 的情况下用赋值语句更新 a 的值即可,上述思想方法称为 “打擂台”。

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 中就存储了最小值,而 b 和 c 中就是另外两个数值,在 c 小于 b 的情况下只需要交换它们的值即可实现

a

<

b

<

c

a < b < c

a<b<c

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 项。

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

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

    sx<t 时,灯是开着的;

  • 如果

    s

    >

    t

    s > t

    s>t,说明开灯和关灯跨越到了第二天,那么当

    t

    x

    <

    s

    t\\le x < s

    tx<s 时,灯是关着的。

虽然上面的方法比较清晰易懂,但是代码较为复杂,留给读者自行实现,下面介绍一种计算时差的方法。先用

t

=

t

s

t=t-s

t=ts 计算出每天开灯的时长

t

t

t,用

x

=

x

s

x=x-s

x=xs 计算出目标时间点距离开灯时间点的时间差

x

x

x。由于时间可能会跨越两天,因此两个时间差都有可能是负数,可以给时间差加上 24 之后再对 24 取余来保证时间差都是非负的。接下来只需要检测目标时间点距离开灯时间点的时间差

x

x

x 是否小于开灯时长

t

t

t 即可。

t = (t s + 24) % 24;
x = (x s + 24) % 24;

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

本节习题

  • P2814 [ABC245A] Good morning
  • P2816 [ABC201A] Tiny Arithmetic Sequence
  • P2196 [ABC103A] Task Scheduling Problem
  • P2827 [ABC253A] Median?
  • P2796 [ABC207A] Repression
  • P1699 [CF6A] Triangle
  • D1424 [25 年 12 月一级] 灯塔
赞(0)
未经允许不得转载:171主机测评 » 第二章 分支结构程序设计(3)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址