文章目录
- 前言
- 一、嵌套循环
- 1.概念
- 2.语法
- 2.1 for循环嵌套for
- 2.2 while循环嵌套for
- 2.3 while循环嵌套while
- 2.4for循环嵌套while
- 2.5 注意
- 2.5.1内层循环之前
- 2.5.2内层循环之后
- 3.按位检查
- 二、例题讲解
- 问题:1448. 随机体能测试
- 问题:1469. 数的统计
- 问题:1511. 数字之和为13的整数
- 问题:1846. 阿尔法乘积
- 三、感谢
- 四、总结
- 五、感谢
前言
探索编程之旅,让我们一起迈入C++的奇妙世界,揭开嵌套循环的神秘面纱。正如冬日里不可或缺的秋裤,嵌套循环在解决复杂问题时担当着关键角色,它让代码的触角延伸至多维数据的每一个角落。跟随小熊的果酱派对之旅,让我们领悟如何利用嵌套循环的魔力,编织出多变的解决方案,确保每一种可能的组合都不被遗漏。
学习路线:C++从入门到NOI学习路线
学习大纲:C++全国青少年信息学奥林匹克竞赛(NOI)入门级-大纲
一、嵌套循环
1.概念
什么是嵌套循环呢?
在C++编程中,嵌套循环指的是在一个循环体内部再包含另一个循环的结构。
这就好像在冬天,我们穿了裤子的情况下又套上了一个秋裤。
什么?你问我为什么有嵌套循环?
怎么说呢,这玩意儿就和冬天需要穿秋裤一样,不穿不得行啊。
因为在实际应用中,单层循环往往无法解决更复杂的问题。
在一个遥远的森林里,有一只可爱的小熊准备举办一场盛大的果酱派对。
它打算邀请所有的森林朋友,而且为了让每位朋友都能尝到不同口味的果酱,小熊决定亲手制作果酱三明治。
小熊有三种不同的面包(小麦面包、燕麦面包、玉米面包),以及四种口味的果酱(草莓、蓝莓、桃子、樱桃)。
小熊想确保每位朋友都能得到所有可能的果酱搭配,如果是你,你会怎么做呢?
这里,嵌套循环的概念就巧妙地融入了小熊的计划之中:
外层循环代表小熊在考虑不同的面包种类,就好比它每次拿起一种面包,准备尝试所有果酱的搭配。
内层循环则代表小熊在给定一种面包的情况下,思考能搭配的所有果酱口味,就像它在每片面包上逐一涂抹不同果酱。
通过这样的规划,小熊能够确保每种面包都能与每种果酱完美结合,创造出了12种独特的果酱三明治组合。这样一来,无论森林里的朋友喜欢什么样的口味,都能找到心仪的三明治。
所以,为什么使用嵌套循环呢?在这个故事里,它帮助小熊系统且高效地探索了所有可能的果酱和面包组合,确保派对上的每一位客人都能享受到个性化的美食体验。
在编程世界中,嵌套循环同样扮演着这样的角色,它帮助我们处理具有多个变量或维度的问题,确保我们能够全面、无遗漏地遍历所有可能性。
2.语法
2.1 for循环嵌套for
语法:
for(初始化表达式1; 条件表达式1; 更新表达式1) {
// 外层循环体
for(初始化表达式2; 条件表达式2; 更新表达式2) {
// 内层循环体
}
}
使用时机:
当需要遍历多维数组、矩阵或其他具有明确边界和计数需求的数据结构时,for循环嵌套非常有用。外层循环通常控制一个维度,内层循环控制另一个维度。
2.2 while循环嵌套for
语法:
while(条件表达式1) {
// 外层循环体
for(初始化表达式2; 条件表达式2; 更新表达式2) {
// 内层循环体
}
}
使用时机:
如果外部循环的终止条件不容易直接通过初始化和更新表达式表示,或者循环次数不确定但需在某个条件满足时停止,可以使用while作为外层循环。内层使用for循环仍然适合于处理确定次数的迭代,比如在每次外层循环中对数组进行遍历。
2.3 while循环嵌套while
语法:
while(条件表达式1) {
// 外层循环体
while(条件表达式2) {
// 内层循环体
}
}
使用时机:
当两个循环的迭代次数都不固定或依赖于运行时条件时,可以使用while循环嵌套。这适用于需要持续检查条件并可能在任意时刻停止的场景,比如处理动态生成的数据序列。
2.4for循环嵌套while
语法:
for(初始化表达式; 条件表达式; 更新表达式) {
// 外层循环体
while(条件表达式) {
// 内层循环体
}
}
使用时机:
这种结构结合了for循环的便利性(特别是在处理已知迭代次数的情况)与while循环的灵活性(对于基于复杂条件或运行时决定的迭代)。当你需要在一个固定次数的外层循环中执行内部操作,而这些内部操作需要基于某些可能在循环过程中变化的条件进行,这时for嵌套while就显得尤为合适。例如,在处理每个元素的同时需要不断检查或等待某个状态改变的场景。
2.5 注意
🔔外层循环内的代码,尤其是在内层循环前后放置的代码,对于控制循环行为和实现特定逻辑至关重要。这部分代码能够影响循环的初始化、条件判断、以及每次迭代之间的交互或最终结果。
2.5.1内层循环之前
- 条件准备:在进入内层循环之前,可能需要根据外层循环的当前状态来调整或计算内层循环所需的条件或参数。
- 资源分配/初始化:如果内层循环需要使用某些资源(如临时变量、文件句柄等),这些准备工作应在内层循环开始前完成。
- 逻辑分支:根据外层循环的迭代情况,可能需要决定是否真的需要进入内层循环,或调整内层循环的行为。
2.5.2内层循环之后
- 累积计算:这是累加、更新或处理内层循环产生的结果的理想位置。例如,如果内层循环计算某个和或计数,可以在外层循环的这一部分汇总这些值。
- 状态更新:根据内层循环的结果或过程中的事件,可能需要更新一些全局或外层循环相关的状态变量。
- 条件检查与跳出:有时,内层循环的执行可能会影响外层循环的继续与否,例如,达到某个提前结束条件时,可以在这里使用break语句跳出外层循环。
3.按位检查
想象你有一个数字,比如学号12345。我们的目标是统计这个数字中有多少个1。为了做到这一点,我们不需要直接查看整个数字,而是可以一位一位地检查。
- 初始化:首先,我们将这个数字赋值给变量t,即t = 12345。
- 检查当前位:接下来,我们进入一个循环,只要t不为0,循环就会继续。这是因为当一个数字除以10的余数为0时,这个数字就变成了0,意味着我们已经检查完了所有的位。
- 检查是否为1:在循环内部,我们使用if(t % 10 == 1)来检查t的个位数(也就是最右边的数字)是否为1。如果是,我们就增加计数器c的值,表示找到了一个’1’。
- 移除当前位:然后,我们用t /= 10;这条语句去掉t的个位数。这相当于把t向右移动了一位。原来12345变成了1234,然后是123,以此类推,直到t变成0,表示所有的位都被检查过了。
- 重复过程:这个过程会重复进行,每一次循环都检查当前数字的最右边一位是不是1,并且每次都去掉这一位,直到整个数字被完全“消耗”掉,即t变成了0。
通过这种方式,无论原始的数字是多少,我们都能准确无误地检查出它里面有多少个数字1,而不会错过任何一个位。
简而言之,这段代码就像拆解一个数字,每次看最右边的一个数字,看看是不是1,然后丢掉这个数字继续看下一个,直到所有的数字都被检查完。这样就能统计出所有出现的数字1的总数。
二、例题讲解
问题:1448. 随机体能测试
类型:循环应用,嵌套循环
题目描述:
学校想随机抽取一部分同学参加体能测试,看看同学们的体能怎么样。
张老师想了一个办法,找出学号中含有 1 的同学,让他们参加体能测试;如果某同学的学号含有 1 个 1 ,那么要测试 1 次,如果含有 2 个 1 ,就要参加 2 次测试。
比如:小明同学的学号为 1211 ,那么他就要参加 3 次测试。
请问,学号 1∼n 中的同学们,总共要参加多少次测试?
输入:
一个整数 n(n≤9999) 。
输出:
一个整数,代表 1∼n 号同学总共要参加测试的次数。
样例:
输入:
11
输出:
4
1.分析问题
- 已知:随机抽取一部分同学参加体能测试。
- 未知:学号 1∼n 中的同学们,总共要参加多少次测试?
- 关系:学号中含有 1 的同学,让他们参加体能测试;如果某同学的学号含有 1 个 1 ,那么要测试 1 次,如果含有 2 个 1 ,就要参加 2 次测试。
2.定义变量
- n:用户输入的正整数,代表范围上限。
- c:计数器,用于累计数字1出现的次数,初始化为0。
- t:临时变量,用于在循环中存储当前处理的数字并逐步分解它来查找数字1。
//二、数据定义
int n,c=0,t;
3.输入数据
- 从标准输入读取一个正整数n。
//三、数据输入
cin>>n;
4.数据计算
使用一个for循环遍历1到n(包括n)。
- 对于每个数i:
- 将i的值赋给t,用于处理这个数字。
- 使用一个while循环处理t,直到t变为0。
- 在while循环内部,使用条件语句if(t % 10 == 1)检查当前数字的个位数是否为1,如果是,则增加计数器c的值。
- 然后,通过t /= 10;将t除以10并向下取整,相当于去掉当前的个位数,继续检查下一位。
//四、数据计算
for(int i=1;i<=n;i++){
t=i;
while(t){
if(t%10==1){
++c;
}
t/=10;
}
}
5.输出结果
- 输出最终计数器c的值,即1到n之间数字1出现的总次数。
//五、输出结果
cout<<c;
return 0;
完整代码如下:
#include <bits/stdc++.h> // 引入常用的STL库,以简化代码编写
using namespace std; // 使用std命名空间,避免需要前缀std::
int main() {
// 问题描述:
// 假设有1到n号同学,需要根据他们的学号中数字'1'的出现次数来决定参加体能测试的次数。
// 学号中每出现一个'1',对应的学生就需要参加一次测试。
// 数据定义:
int n, c = 0, t; // n: 学生总数, c: 测试总次数(初始化为0), t: 临时变量,用于逐位检查学号
// 数据输入:
cin >> n; // 从用户那里获取学生总数n
// 数据计算:
// 遍历每一个学生的学号,从1到n
for(int i = 1; i <= n; i++) {
t = i; // 将当前学生学号i赋值给t,开始检查
// 使用带余除法逐位检查学号中的每一个数字是否为1
while(t) {
if(t % 10 == 1) { // 如果当前位数字是1
++c; // 测试次数加一
}
t /= 10; // 去掉当前最低位,检查下一位
}
}
// 输出结果:
// 打印总共需要参加测试的次数
cout << c;
return 0; // 主函数正常结束
}
问题:1469. 数的统计
类型:嵌套循环
题目描述:
试计算在区间 1 到 n 的所有整数中,数字 x ( 0≤x≤9 )共出现了多少次?
例如,在 1 到 11 中,即在 1,2,3,4,5,6,7,8,9,10,11 中,数字 1 出现了 4 次。
再比如,1 到 11 中,数字 0 只出现了 1 次。
输入:
2 个整数 n 和 x 之间用一个空格隔开。( n 是一个 int 范围内的整数)
输出:
1 个整数,表示 x 出现的次数。
样例:
输入:
11 1
输出:
4
1.分析问题
- 已知:1-n的所有整数,数字x;
- 未知:数字x在1-n中出现的次数 。
- 关系:
2.定义变量
- 声明了三个整型变量,n代表上界(即1到n之间的n),x是要查找的数字,c用于计数x出现的次数,初始化为0。
//二、数据定义
int n,x,c=0;
3.输入数据
- 从标准输入接收两个整数,第一个是n,第二个是x。
//三、数据输入
cin>>n>>x;
4.数据计算
通过两层循环实现:
- 外层循环:for(int i=1; i<=n; i++),遍历1到n之间的每个整数。
- 内层循环(实际上是处理单个整数的循环):while(temp),当temp非零时继续循环,用于逐位检查当前整数的每一位是否等于x。
- temp=i;:将当前遍历到的整数赋值给temp。
- if(temp%10==x) ++c;:判断temp的个位数是否等于x,如果是,则c加1。
- temp/=10;:将temp除以10并去掉个位数,以便检查下一位。
//四、数据计算
int temp;
for(int i=1;i<=n;i++){
temp=i;
while(temp){
if(temp%10==x) ++c;
temp/=10;
}
}
5.输出结果
- 输出数字x在1到n之间出现的总次数。
//五、输出结果
cout<<c;
完整代码如下:
#include <bits/stdc++.h> // 引入标准库文件,包含了很多常用的头文件,方便编程
using namespace std; // 使用标准命名空间std,省略后续代码中的std::前缀
int main() {
// 一、问题分析:
// 已知条件:一个整数范围1到n,以及一个目标数字x。
// 未知:数字x在1到n之间所有整数里出现的总次数。
// 关系:需要遍历1到n之间的每个数,检查每个数里数字x出现的次数并累加。
// 二、数据定义:
int n, x, c = 0; // n: 上限值, x: 目标数字, c: 计数器(记录x出现的次数)
// 三、数据输入:
cin >> n >> x; // 从用户处读取n和x的值
// 四、数据计算:
int temp; // 临时变量,用于分解当前处理的数字
for(int i = 1; i <= n; i++) { // 遍历1到n之间的每个整数
temp = i; // 将当前数i赋值给temp,准备分解
while(temp) { // 当temp不为0时,继续循环
if(temp % 10 == x) ++c; // 检查temp的个位数是否等于x,如果是则c加1
temp /= 10; // temp除以10,丢弃个位,继续检查下一位
}
}
// 五、输出结果:
cout << c; // 输出数字x在1到n之间出现的总次数
return 0; // 程序执行成功结束,返回0
}
问题:1511. 数字之和为13的整数
类型:简单循环、嵌套循环
题目描述:
求出 1∼n 范围内的整数,使其数字之和为 13,请问这样的数有多少个?
例如:数 85 ,其数字之和为 8+5=13 ;数 373,其数字之和为 3+7+3=13 。
输入:
一个整数 n(n≤10000000);
输出:
输出一个整数,代表符合条件数的总个数。
样例:
输入:
1000
输出:
75
1.分析问题
- 已知:1-n所有的整数;
- 未知:符合条件的数的个数;
- 关系:各数位之和为13;
2.定义变量
- n表示上界,即考虑的整数范围到n为止;c用于计数满足条件的数的数量,初始化为0。
//二、数据定义
int n,c=0;
3.输入数据
- 从用户输入获取整数n,作为数的上界。
//三、数据输入
cin>>n;
4.数据计算
双重循环结构实现:
- 外层循环:for(int i=1; i<=n; i++),遍历1到n之间的每个整数。
- 内层逻辑:
- temp临时存储当前遍历到的数,sum用于累加当前数的各位数字之和。
- 当temp不为0时进入循环,逐位相加。
- 累加temp的个位数到sum。
- 去掉temp的个位数,继续处理下一位。
- 如果累加和等于13,则符合条件,c自增。
//四、数据计算
int temp,sum;
for(int i=13;i<=n;i++){
temp=i;
sum=0;
while(temp){
sum+=temp%10;
temp/=10;
}
if(13==sum) ++c;
}
5.输出结果
- 输出符合条件的数的个数。
//五、输出结果
cout<<c;
完整代码如下:
#include <bits/stdc++.h> // 包含大量常用头文件的预编译库,方便编程但可能影响编译速度
using namespace std; // 使用std命名空间,可以直接调用std中的函数和对象,无需前缀
int main() {
// 问题分析:
// 已知条件:考虑1到n范围内的所有整数。
// 目标:找出所有数位之和等于13的整数,并统计这些数的总数。
// 关系:需要遍历每个数,计算其各位数字之和,并检查是否等于13。
// 数据定义:
int n, c = 0; // n: 上界,即考虑的整数范围最大值, c: 计数器,记录符合条件的数的数量
// 数据输入:
cin >> n; // 从标准输入读取整数n,作为上界
// 数据计算:
int temp, sum; // temp用于暂存当前遍历的数,sum用于计算当前数的位数之和
for(int i = 1; i <= n; i++) { // 遍历从1到n的每一个整数
temp = i; // 将当前遍历的数i赋值给temp
sum = 0; // 初始化sum为0,用于累加当前数的位数和
while(temp) { // 当temp不为0时循环,即继续处理当前数的位
sum += temp % 10; // 取temp的个位数并加到sum上
temp /= 10; // 去掉temp的个位数,继续处理下一位
}
if(13==sum ) ++c; // 如果当前数的位数之和等于13,则c计数器加1
}
// 输出结果:
cout << c; // 输出满足条件的数的数量
return 0; // 程序执行完毕,返回0表示成功
}
问题:1846. 阿尔法乘积
类型:嵌套循环,循环应用
题目描述:
计算一个整数的阿尔法乘积。对于一个整数 x 来说,它的阿尔法乘积是这样来计算的:如果 x 是一个个位数,那么它的阿尔法乘积就是它本身;否则的话,x 的阿尔法乘积就等于它的各位非 00 的数字相乘所得到的那个整数的阿尔法乘积。
例如:4018224312 的阿尔法乘积等于 8 ,它是按照以下的步骤来计算的:
4018224312 → 4×1×8×2×2×4×3×1×2 → 3072 → 3×7×2 → 42 → 4×2 → 8;
编写一个程序,输入一个正整数(该整数的值在 int 范围内),输出它的阿尔法乘积。
输入:
输入只有一行,即一个正整数。
输出:
输出相应的阿尔法乘积。
样例:
输入:
3072
输出:
8
1.分析问题
- 已知:一个整数;
- 未知:它的连续乘积,直到结果为单个数字;
- 关系:通过循环不断取整数的最后一位与结果相乘,同时去掉已经处理的数字。
2.定义变量
- 定义整型变量n,用于存储用户输入的整数。
- 定义整型变量temp和a,分别用于临时存储整数和累积乘积。
//二、数据定义
int n;
int temp,a;
3.输入数据
- 使用cin>>n;从标准输入读取一个整数到变量n中。
//三、数据输入
cin>>n;
4.数据计算
- 外层循环:while(n >= 10)确保当整数至少为两位时进入循环。如果整数只剩一位且非零,自然就是其自身的“阿尔法乘积”,无需进一步计算。
- 内层循环:对于当前的n,使用temp临时保存其值,a初始化为1,用于累积乘积。
- 在内层循环中,while(temp)确保当temp非零时继续循环。
- if (temp % 10 != 0)检查当前位是否为零,如果不为零,则将该位数字乘以a的当前值并更新a。
- temp /= 10;每次循环结束时,将temp除以10,丢弃最右边的一位数字,以便下一轮循环处理下一位。
- 当内层循环结束后,n的值被更新为a,即当前阶段的非零数字乘积。外层循环继续,直到n减少到小于10,此时n即为最终的“阿尔法乘积”。
//四、数据计算
int temp,a;
while(n>=10){
temp=n;
a=1;
while(temp){
if (temp%10!=0) a*=temp%10;
temp/=10;
}
n=a;
}
5.输出结果
- 输出计算得到的“阿尔法乘积”。
//五、输出结果
cout<<n;
完整代码如下:
#include<bits/stdc++.h>
// 引入头文件,包含了C++标准库中的所有常用组件,简化了单独引入每个所需头文件的过程。但这种做法在实际开发中并不推荐,因为它会增加编译时间和代码体积,通常建议只引入实际需要的头文件。
using namespace std;
// 使用std命名空间,这样就可以直接使用std中的函数和对象,而不需要每次都加上std::前缀。
int main(){
// 程序的主函数入口。
// 一、分析问题
// 已知:一个整数;
// 未知:它的连续乘积,直到结果为单个数字;
// 关系:通过循环不断取整数的最后一位与结果相乘,同时去掉已经处理的数字。
// 二、数据定义
int n;
// 定义整型变量n,用于存储待处理的整数。
// 三、数据输入
cin>>n;
// 从标准输入读取一个整数到变量n中。
// 四、数据计算
int temp, a;
// 定义临时变量temp用于存储处理过程中的整数,a用于存储当前的乘积结果。
while(n >= 10){
// 当n大于等于10时,继续循环,因为单个数字不需要再处理。
temp = n;
a = 1;
// 初始化temp为n,a为1,开始新的乘积计算。
while(temp){
// 当temp不为0时,循环处理每一位数字。
if (temp % 10 != 0) a *= temp % 10;
// 如果当前位数字不为0,则与a相乘。
temp /= 10;
// 将temp除以10,去掉已处理的最低位。
}
n = a;
// 将当前的乘积结果赋给n,继续下一轮处理,直到n变成一个一位数。
}
// 五、输出结果
cout<<n;
// 输出最终的单个数字,即所有非零数字连续乘积的结果。
return 0;
// 主函数结束,返回0表示程序正常执行完毕。
}
三、感谢
如若本文对您的学习或工作有所启发和帮助,恳请您给予宝贵的支持——轻轻一点,为文章点赞;若觉得内容值得分享给更多朋友,欢迎转发扩散;若认为此篇内容具有长期参考价值,敬请收藏以便随时查阅。
每一次您的点赞、分享与收藏,都是对我持续创作和分享的热情鼓励,也是推动我不断提供更多高质量内容的动力源泉。期待我们在下一篇文章中再次相遇,共同攀登知识的高峰!
四、总结
嵌套循环,编程艺术中的精妙构造,以其独特的方式解锁了多层面问题处理的大门。通过本次学习,我们不仅掌握了循环嵌套的语法精髓,还通过实例深入理解了其在实际问题求解中的广泛应用,如同细致拆分数字以精确计数,或是安排学号含"1"同学的体能测试次数。这不仅是一场技术的洗礼,更是逻辑思维的锻炼。未来在编程征途中,愿嵌套循环成为你手中锋利的刃,助你精准驾驭复杂逻辑,高效征服算法挑战。
五、感谢
如若本文对您的学习或工作有所启发和帮助,恳请您给予宝贵的支持——轻轻一点,为文章点赞;若觉得内容值得分享给更多朋友,欢迎转发扩散;若认为此篇内容具有长期参考价值,敬请收藏以便随时查阅。
每一次您的点赞、分享与收藏,都是对我持续创作和分享的热情鼓励,也是推动我不断提供更多高质量内容的动力源泉。期待我们在下一篇文章中再次相遇,共同攀登知识的高峰!