第 15 章 岔路口上跑圈——选择与重复的联合作战
🏗️ 前情回顾:上章你问了「循环套循环跑到一半想停怎么办」——break 和 continue 就是答案。现在条件和循环你都能分开了,但真实问题从来不分家——猜数字要循环套分支,菜单要分支套循环。这一章,我们把 M2 所有武器拧成一股绳,练到**"看到问题就能选对结构"**。
🎯 本章目标
学完这一章,你能:
- 面对一道新题,快速判断该用分支还是循环(还是两者结合)
- 用
if/else+while实现猜数字游戏 - 用
switch+ 循环实现菜单驱动程序 - 用嵌套循环打印各种星号图案
- 用循环判断水仙花数、完数、质数
- 建立起"选择结构"的条件反射
📖 故事引入
小华参加校内的编程兴趣小组。老师出了一道题:"判断一个数是不是质数。"
小华心里想:"质数?就是只能被 1 和自己整除的数……等等,程序怎么判断'只能'?"
他用 if (n % 2 != 0) 试了一下——不行,9 不是质数但也能通过。他又加了 if (n % 3 != 0)……加到什么时候是个头?
旁边的学长扫了一眼:"你想想,怎么让程序帮你逐个试除数?"
"逐个试……逐个……"小华眼睛一亮——for 循环!从 2 试到 n-1,只要有一个能整除就不是质数。
这就是"看到问题→选择结构"的思维。不是先去想代码怎么写,而是先问自己三个问题:
- 需不需要按条件分岔?→ 用
if/switch - 需不需要做重复的事?→ 用
for/while - 分岔里面是不是还有分岔?重复里面是不是还有重复?→ 嵌套
🧱 知识讲解
15.1 思维框架:三步选结构
每见一道题,先不写代码,按这三步走:
| 步骤 | 问题 | 答案 → 结构 |
|---|---|---|
| ① | 需要根据条件走不同岔路吗? | 是 → if/else 或 switch |
| ② | 需要重复做某件事吗? | 是 → for(已知次数) / while(未知次数) |
| ③ | 岔路里还要重复?重复里还要岔路? | 是 → 嵌套 |
先画流程图(哪怕纸上画几个框),再翻译成代码。这个习惯帮你少走一半弯路。
15.2 经典题①:猜数字游戏
题目:程序随机生成一个 1~100 的整数,玩家每次猜一个数,程序回复"大了"或"小了",直到猜对。
思路分析:
- 需要重复猜直到猜对 →
while循环 - 每次猜测后判断大小 →
if/else分支 - 结论:循环套分支
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
srand(time(0));
int secret = rand() % 100 + 1; // 1~100 的秘密数字
int guess, cnt = 0;
cout << "我想好了一个 1~100 之间的数字,你猜猜看?" << endl;
while (true) {
cout << "你的猜测:";
cin >> guess;
cnt++;
if (guess > secret) {
cout << "太大了!再试试。" << endl;
} else if (guess < secret) {
cout << "太小了!再试试。" << endl;
} else {
cout << "恭喜猜对!你一共猜了 " << cnt << " 次。" << endl;
break;
}
}
return 0;
}💡 用
cnt统计猜测次数——这个计数器模式在循环中极其常用。
15.3 经典题②:循环菜单计算器
题目:程序显示菜单(1.加法 2.减法 3.乘法 4.除法 0.退出),用户选择后输入两个数计算结果,算完回到菜单,选 0 退出。
思路分析:
- 菜单需要反复显示直到用户选 0 →
while循环 - 选不同数字做不同运算 →
switch分支 - 结论:循环套 switch
#include <iostream>
using namespace std;
int main() {
int choice;
double a, b;
while (true) {
cout << "\n=== 计算器菜单 ===" << endl;
cout << "1. 加法 2. 减法 3. 乘法 4. 除法 0. 退出" << endl;
cout << "请选择:";
cin >> choice;
if (choice == 0) {
cout << "再见!" << endl;
break;
}
if (choice < 1 || choice > 4) {
cout << "无效选择,请重试。" << endl;
continue; // 跳过本次循环,重新显示菜单
}
cout << "输入两个数:";
cin >> a >> b;
switch (choice) {
case 1: cout << a << " + " << b << " = " << a + b << endl; break;
case 2: cout << a << " - " << b << " = " << a - b << endl; break;
case 3: cout << a << " * " << b << " = " << a * b << endl; break;
case 4:
if (b == 0) cout << "错误:除数不能为 0!" << endl;
else cout << a << " / " << b << " = " << a / b << endl;
break;
}
}
return 0;
}🎯 这个程序融合了 M1(输入输出、运算)和 M2(分支、循环)几乎所有知识——它就是一个完整的"应用程序"雏形。
15.4 经典题③:打印图案
题目:打印一个 n 行的直角三角形:
*
**
***
****思路:外层循环控制行数,内层循环控制每行打印几个星号。
int n;
cin >> n;
for (int i = 1; i <= n; i++) { // i:第几行
for (int j = 1; j <= i; j++) { // j:这行打几个 *
cout << "*";
}
cout << endl; // 行末换行
}🧩 嵌套循环的核心规律:外层走一行,内层跑完一轮。你可以想象外层是时针(慢),内层是秒针(快)。
15.5 经典题④:水仙花数
定义:一个三位数等于它各位数字的立方和。如 153 = 1³ + 5³ + 3³。
思路:枚举所有三位数(100~999),拆出个位、十位、百位,判断。
for (int n = 100; n <= 999; n++) {
int ge = n % 10; // 个位
int shi = n / 10 % 10; // 十位
int bai = n / 100; // 百位
if (ge * ge * ge + shi * shi * shi + bai * bai * bai == n) {
cout << n << " 是水仙花数" << endl;
}
}跑一遍,你会发现四个水仙花数:153、370、371、407。
15.6 经典题⑤:质数判断
题目:判断一个大于 1 的整数 n 是不是质数。
核心优化:不用从 2 试到 n-1,只需试到 √n!因为如果 n 有大于 √n 的因子,必定对应一个小于 √n 的因子。
bool isPrime = true;
if (n <= 1) isPrime = false;
for (int i = 2; i * i <= n; i++) { // i² ≤ n,等价于 i ≤ √n
if (n % i == 0) {
isPrime = false;
break; // 找到一个因子就不必继续
}
}
cout << (isPrime ? "是质数" : "不是质数") << endl;💡
i * i <= n替代i <= sqrt(n),避免了浮点误差,是竞赛常用写法。
15.7 经典题⑥:完数判断
定义:一个数等于它所有真因子(除了自己以外的因子)之和。如 6 = 1 + 2 + 3。
int n, sum = 0;
cin >> n;
for (int i = 1; i < n; i++) {
if (n % i == 0) sum += i;
}
if (sum == n) cout << n << " 是完数" << endl;
else cout << n << " 不是完数" << endl;✋ 动手试试
试试 1:把 15.4 的图案改成等腰三角形(每行前面加空格让它居中)。提示:第 i 行前面打印 n - i 个空格。
试试 2:运行猜数字游戏,故意二分查找(每次猜中间值),看看最多几轮能猜中 1~100 范围的数字?这其实揭示了二分查找的威力——最多 7 次。
试试 3:把循环菜单计算器的 switch 改成 if/else if 实现,感受两种写法的差异。
⚠️ 你踩过这些坑吗
- [ ] 坑 1:循环条件写错导致死循环——
while (choice != 0)但没有在循环里改choice→ 程序永远停不下来。确保循环条件中的变量会改变,或者用break跳出。 - [ ] 坑 2:
break只跳出最近一层——嵌套循环的内层 break 只关内层的事,外层照样跑。想全停用标记变量。 - [ ] 坑 3:嵌套循环内外变量名混淆——内外层都用
i→ 内层"遮蔽"外层,逻辑错乱。习惯用i外层、j内层、k更内层。 - [ ] 坑 4:质数判断忘了 1 不是质数——
n = 1时输出"是质数"。先判断n <= 1直接返回不是质数。
📝 练习
基础题
1. 写出输出:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 4; j++) {
cout << "*";
}
cout << endl;
}2. 选择题:猜数字游戏用哪种循环最合适? A. for B. while C. do-while D. 随便
3. 判断:153 是不是水仙花数?用手算验证。
4. 填空题
(1)判断质数时,循环只需要试到 ______(用数学符号表示),代码写成 i * i <= n。
(2)猜数字 = ______ 循环套 ______ 分支;计算器菜单 = ______ 循环套 ______ 分支。
提高题
5. 编程题 — 九九乘法表
打印标准九九乘法表:
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
...6. 编程题 — 找出 1~1000 内所有完数
(提示:1~1000 只有三个完数:6、28、496。)
7. 编程题 — 打印菱形
输入一个奇数 n,打印由 * 组成的菱形。如 n=5:
*
***
*****
***
*挑战题
8. 编程题 — 猜数字·增强版
在基础猜数字上增加:①限制最多 10 次猜测,超过显示"你输了";②记录历史猜测,猜完后显示所有猜测过程;③支持"再来一局"功能。
9. 编程题 — 哥德巴赫猜想验证
输入一个大于 2 的偶数 n,找出两个质数使它们的和等于 n(如 10 = 3 + 7)。如果有多组解,输出质数差最大的一组。
🧠 本章小结
M2 综合实战 —— 结构选择思维:
【三步选结构】
① 需要分岔? → if/else 或 switch
② 需要重复? → for(已知次数)/ while(未知次数)
③ 需要嵌套? → 画流程图再写代码
【经典题型】
猜数字 → while + if/else
循环菜单 → while + switch
打印图案 → 嵌套 for(外层行,内层列)
水仙花数 → for 枚举 + 拆位 + if 判定
质数判断 → for 试除(√n 优化)+ 标记法 + break
完数判断 → for 找因子 + 累加比较
【核心技巧】
计数器 cnt | 标记 bool | break 提前退出 | i*i <= n 平方根优化📝 配套练习
共11题。M2收官。每道题同时用循环+分支,检验M2所有技能的实际搭配能力。。★核心(课堂必做) ◆拓展(课后练习) ★★★挑战(选做)
| 级别 | 题号 | 链接 | 覆盖知识点 |
|---|---|---|---|
| ★ 核心 | J0064 | https://hydro.ac/d/srqc/p/J0064 | for+if+累加、月度模拟 |
| ◆ 拓展 | J0062 | https://hydro.ac/d/srqc/p/J0062 | for、最值、两遍遍历 |
| ◆ 拓展 | J0068 | https://hydro.ac/d/srqc/p/J0068 | for、标记数组、区间操作 |
| ◆ 拓展 | J0072 | https://hydro.ac/d/srqc/p/J0072 | 嵌套for、规律填充、幻方 |
| ◆ 拓展 | J0037 | https://hydro.ac/d/srqc/p/J0037 | 字符串、if-else、模运算 |
| ◆ 拓展 | J0038 | https://hydro.ac/d/srqc/p/J0038 | if-else嵌套、三角形分类 |
| ◆ 拓展 | J0041 | https://hydro.ac/d/srqc/p/J0041 | if-else、三边关系、排序 |
| ◆ 拓展 | luogu-P1046 | https://hydro.ac/p/luogu-P1046 | for+if、计数、简单模拟 |
| ◆ 拓展 | luogu-P1089 | https://hydro.ac/p/luogu-P1089 | for+if、预算模拟、break |
| ◆ 拓展 | luogu-P1423 | https://hydro.ac/p/luogu-P1423 | while、累加、阈值 |
| ★★★ 挑战 | luogu-P1009 | https://hydro.ac/p/luogu-P1009 | for、累加、高精度加法 |
| ◆ 拓展 | luogu-P1085 | https://hydro.ac/p/luogu-P1085 | NOIP2004、for+if、最值 |
| ◆ 拓展 | luogu-P1548 | https://hydro.ac/p/luogu-P1548 | NOIP1997、嵌套for、计数 |
| ◆ 拓展 | luogu-P1146 | https://hydro.ac/p/luogu-P1146 | 状态翻转、多轮操作 |
| ◆ 拓展 | luogu-P2669 | https://hydro.ac/p/luogu-P2669 | NOIP2015、for嵌套、累加 |
💡 练习建议:先完成 2 道★核心题,确保掌握本章基本方法;再完成 13 道◆拓展题,覆盖不同变式和细节;★★★挑战题建议在完成拓展题后再做,涉及多知识点综合。
配套练习
共11题。M2收官。每道题同时用循环+分支,检验M2所有技能的实际搭配能力。★核心(课堂必做) · ◆拓展(课后练习) · ★★★挑战(选做)
| 级别 | 题号 | 链接 | 覆盖知识点 |
|---|---|---|---|
| ★ 核心 | J0064 | https://hydro.ac/d/srqc/p/J0064 | for+if+累加、月度模拟 |
| ◆ 拓展 | J0062 | https://hydro.ac/d/srqc/p/J0062 | for、最值、两遍遍历 |
| ◆ 拓展 | J0068 | https://hydro.ac/d/srqc/p/J0068 | for、标记数组、区间操作 |
| ◆ 拓展 | J0072 | https://hydro.ac/d/srqc/p/J0072 | 嵌套for、规律填充、幻方 |
| ◆ 拓展 | J0037 | https://hydro.ac/d/srqc/p/J0037 | 字符串、if-else、模运算 |
| ◆ 拓展 | J0038 | https://hydro.ac/d/srqc/p/J0038 | if-else嵌套、三角形分类 |
| ◆ 拓展 | J0041 | https://hydro.ac/d/srqc/p/J0041 | if-else、三边关系、排序 |
| ◆ 拓展 | luogu-P1046 | https://hydro.ac/p/luogu-P1046 | for+if、计数、简单模拟 |
| ◆ 拓展 | luogu-P1089 | https://hydro.ac/p/luogu-P1089 | for+if、预算模拟、break |
| ◆ 拓展 | luogu-P1423 | https://hydro.ac/p/luogu-P1423 | while、累加、阈值 |
| ★★★ 挑战 | luogu-P1009 | https://hydro.ac/p/luogu-P1009 | for、累加、高精度加法 |
| ◆ 拓展 | luogu-P1085 | https://hydro.ac/p/luogu-P1085 | NOIP2004、for+if、最值 |
| ◆ 拓展 | luogu-P1548 | https://hydro.ac/p/luogu-P1548 | NOIP1997、嵌套for、计数 |
| ◆ 拓展 | luogu-P1146 | https://hydro.ac/p/luogu-P1146 | 状态翻转、多轮操作 |
| ◆ 拓展 | luogu-P2669 | https://hydro.ac/p/luogu-P2669 | NOIP2015、for嵌套、累加 |
练习建议:先在课堂完成 1 道★核心题,掌握本章基本方法;课后完成 13 道◆拓展题,覆盖不同变式和细节;★★★挑战题建议在完成拓展题后再做,涉及多知识点综合。
自查清单:
- [ ] 我能面对新题快速判断该用什么结构
- [ ] 我能独立写出猜数字游戏的完整代码
- [ ] 我能用
switch+ 循环做菜单程序 - [ ] 我能用嵌套循环打印任意星号图案
- [ ] 我会判断水仙花数、完数、质数
- [ ] 我知道
break只跳出一层循环,多层用标记变量 - [ ] 我会用
i * i <= n做平方根优化
🚀 M2 完结! 你已经能让程序判断、重复、嵌套、刹车、跳过——控制力上了五个台阶。但到目前为止,你的程序一次只能处理一个数。下一站 M3「循环与数据容器」,学会用数组一口气记下成百上千个数据——像打开一整排柜子一样。