一.多层循环
①(代码)输出每个数的真约数
任务描述
编程输入两个整数a和b(1<a<b),对于整数区间[a,b]内的所有整数x,依次输出x的所有真约数。
输入格式
一行中两个整数a和b,空格分隔。
输出格式
[a,b]区间内每个整数x输出一行,先输出x和冒号,然后依次输出它的所有真约数,约数间以一个空格分隔。
输入样例:
100 110
输出样例:
100:1 2 4 5 10 20 25 50
101:1
102:1 2 3 6 17 34 51
103:1
104:1 2 4 8 13 26 52
105:1 3 5 7 15 21 35
106:1 2 53
107:1
108:1 2 3 4 6 9 12 18 27 36 54
109:1
110:1 2 5 10 11 22 55
相关知识 循环嵌套
循环结构的循环体是一个语句或是一个复合语句,当然这个语句或复合语句中也可以是另外一个循环结构。如果是这样,就构成了循环结构的嵌套。
三种循环结构可以互相嵌套,例如:
![](https://i-blog.csdnimg.cn/blog_migrate/ab62533b756cef520e85b7c3915321c0.png)
以上列出了6种常见的循环嵌套情况,实际上还有很多种嵌套的情形。这也只是两层循环嵌套,还有3层甚至更多层次的循环嵌套在一起。
任务分析
对于这个任务,很显然对于[a,b]区间的每一个数x,我们要穷举(一一列举)处理,于是我们得到以下代码框架:
程序代码 外层循环(程序框架)
#include<stdio.h>
int main()
{
int a,b,x,i;
scanf("%d%d",&a,&b); //输入整数a,b
for(x=a;x<=b;x++) //外层循环穷举所有的x
{
//处理x:输出x、冒号及x的所有真约数和回车
}
return 0;
}
接下来,我们就不需要考虑外层循环,只要思考如何处理x,输出x的所有真约数就可以了。这样,我们就把一个相对高维复杂的问题,降维分解为两个相对简单的问题。外层循环内,“输出x、冒号及x的所有真约数和回车”的问题,我们是熟悉的,可以通过下面的循环代码解决。
程序代码 内层循环(处理x)
printf("%d:",x); //输出x和冒号
for(i=1;i<x;i++) //内层循环输出n的所有真约数穷举[1,n-1]
if(x%i==0) //是约数就输出
{
if(i>1)printf(" "); //大于1的约数前才输出空格
printf("%d",i);
}
printf("\n"); //输出回车
内层循环的代码,我们非常好理解,就是通过穷举[1,n-1]的所有整数,找到真约数(小于本身的约数)就输出,通过if语句控制空格的输出,这些方法都是我们已经熟悉的。我们把以上内层循环代码放到外层循环中,就得到完整代码。
程序代码 双层循环(完整代码)
#include<stdio.h>
int main()
{
int a,b,x,i;
scanf("%d%d",&a,&b); //输入整数a,b
for(x=a;x<=b;x++) //外层循环穷举所有的x
{
//处理x:输出x、冒号及x的所有真约数和回车
printf("%d:",x); //输出x和冒号
for(i=1;i<x;i++) //内层循环输出n的所有真约数穷举[1,n-1]
if(x%i==0) //是约数就输出
{
if(i>1)printf(" "); //大于1的约数前才输出空格
printf("%d",i);
}
printf("\n"); //输出回车
}
return 0;
}
代码测试与分析
输入:2 8 (可能的最小值2)输出:2:13:14:1 25:16:1 2 37:18:1 2 4输入:100 101 (可能的最小跨度)输出:100:1 2 4 5 10 20 25 50101:101
以上程序就是最典型的双层循环结构的代码,我们通过分析将问题降维分解为两个单层循环问题:外层循环:只负责穷举x,用单层for循环轻易解决,逻辑非常简单;内层循环:只负责输出x的真约数,通过穷举[1,n-1]的整数,用单层for循环可轻易解决,逻辑同样非常简单。可见,多重循环并不可怕,复杂的问题降维分解为若干简单问题的方法,我们要掌握。这样,编程就像搭积木一样简单。
②(代码)九九乘法表
任务描述
编程应用双层循环输出九九乘法表。
#include<stdio.h>
int main()
{
int i, j, t;
for (i = 1;i <= 9;i++) //外层循环穷举变量i从1至9
{
//处理每一行(第i行)
for (j = 1;j <= i;j++) //内层循环输出第i行乘法表(j从1穷举到i共i项)
{
t = i * j;
printf("%d*%d=%d", j, i, j * i);//输出一项(第i行第j列)
if (j < i) //控制每一项后面空格的输出
{
if (t < 10)
{
printf(" "); //积小于10输出2个空格
}
}
else
{
printf(" "); //否则输出1个空格
}
}
printf("\n"); //输出回车
}
return 0;
}
以上程序同样也是最典型的双层循环结构的代码,我们同样通过分析将问题降维分解为两个单层循环问题:外层循环:只负责穷举i从1到9,用单层for循环轻易解决,逻辑非常简单;内层循环:只负责输出第i行的乘法表(共i项),通过让j穷举从1到i,用单层for循环可轻易解决,逻辑同样非常简单。也可以简单理解为,外层循环负责控制行号,内层循环负责控制列号,内层循环体负责输出第i行第j列的算式。
③(代码)区间内素数
任务描述
编程输入两个整数a,b(2<=a<b),输出整数区间[a,b]内的所有素数(测试数据中保证区间内有素数)。
输入格式
两个整数a和b。
输出格式
区间[a,b]内的所有素数,逗号分隔。
#include<stdio.h>
#include<math.h>
int main()
{
int a, b, n, i, f, k;
scanf("%d%d", &a, &b); //输入a,b
k = 0; //计数器清0
for (n = a;n <= b;n++) //外层循环穷举区间[a,b]内所有的n
{
//如果n是素数就输出
f = 1; //标志变量赋初值1
for (i = 2;i <= sqrt(n);i++) //穷举2到根号n找约数
{
if (n % i == 0) //找到约数就f=0并跳出
{
f = 0;
break;
}
}
if (f == 1) //如果n为素数
{
++k; //计数器计数
if (k >= 2)printf(","); //从第2个素数起前面才加逗号
printf("%d", n); //后输出素数
}
}
return 0;
}
使用变量k进行计数,是一个非常好用的方法和技巧,请注意要在外层循环之前进行清0操作,然后在内层循环内,如果找到素数首先立即执行++k,实现计数,并保证找到第一个素数时,k的值为1。请思考:为什么程序中先输出逗号,后输出素数,能不能先输出素数,然后再控制输出逗号呢?答案是不能的,因为我们无法得知哪一个是最后的素数,也就无法实现控制最后一个素数后不输出逗号。但是,我们可以通过给素数计数的方式,准确得知谁是第一个素数,也就可以方便地控制第一个素数前不输出逗号,而其它素数前都输出逗号。
④(代码)菱形图案
任务描述
请编程输入一个奇数n(n<100)和一个字符c,输出n行由字符c组成的菱形图案。
输入样例:
5 A
输出样例:
A
AAA
AAAAA
AAA
A
#include<stdio.h>
int main()
{
int i, j, n, k = 1;
char c;
scanf("%d %c", &n, &c);
for (i = 1;i <= n / 2 + 1;i++) //中间和上半部分的输出
{
for (j = 1;j <= (n / 2 + 1) - i;j++) //找规律输出空格
{
printf(" "); //以下规律自己找,能通过就行
}
for (j = 1;j <= 2 * i - 1;j++) //输出空格后输出字符
{
printf("%c", c);
}
printf("\n");
}
for (i = n / 2 + 2;i <= n;i++)
{
for (j = 1;j <= i - (n / 2 + 1);j++)
{
printf(" ");
}
for (j = (i - 2 * k) * 2 - 1;j >= 1;j--)
{
printf("%c", c);
}
k++;
printf("\n");
}
return 0;
}
⑤(代码)孪生素数
任务描述
孪生素数就是指相差2的素数对,例如3和5,5和7,11和13…,已经证明孪生素数存在无穷多对。
编程输入正整数a(10000>=a>=2),输出不小于a的第一对孪生素数。差是2的两个素数被称为孪生素数。
输入样例:
10000
输出样例:
10007 10009
#include<stdio.h>
#include<math.h>
int main()
{
int a, b, i, j;
scanf("%d", &a);
for (i = a;i <= 10009;i++) //据题 最大的输出为10009
{
b = i + 2;
for (j = 2;j <= sqrt(i);j++)
{
if (i % j == 0 || b % j == 0)
{
break;
}
}
if (j > sqrt(i) && i != 2) //i==2时正好跳出,但此时b==4
{
break;
}
}
printf("%d %d", i, b);
return 0;
}
二.处理多组数据(确定组数)
①(代码)奥运奖牌计数
任务描述
2008年北京奥运会,A国的运动员参与了n天的决赛项目(1≤n≤17)。现在要统计一下A国所获得的金、银、铜牌数目及总奖牌数。
输入格式:
输入n+1行,第1行是A国参与决赛项目的天数n,其后n行,每一行是该国某一天获得的金、银、铜牌数目,以一个空格分开。
输出格式:
输出1行,包括4个整数,为A国所获得的金、银、铜牌总数及总奖牌数,以一个空格分开。
输入样例:
3
1 0 3
3 1 0
0 3 0
输出样例:
4 4 3 11
任务分析 确定组数的多组数据
可以看出,任务要求我们处理多组数据(多组奖牌得数),数据的组数是确定的,在输入数据中最先直接给出。所以,我们可以构造一个次数固定的计次循环,在循环体内处理每组数据。问题逻辑变得相对简单。
代码:
#include<stdio.h>
int main()
{
int n, a, b, c, i;
int sum_a, sum_b, sum_c, sum;
sum_a = sum_b = sum_c = 0; //金银铜牌数量清0
scanf("%d", &n); //首先读入数据组数n
for (i = 1;i <= n;i++) //构造n次循环处理每组数据
{
//处理每组数据,读入、统计累加
scanf("%d%d%d", &a, &b, &c);//读入一组数据
sum_a += a; //分别统计
sum_b += b;
sum_c += c;
}
sum = sum_a + sum_b + sum_c; //统计总数
printf("%d %d %d %d", sum_a, sum_b, sum_c, sum); //输出结果
return 0;
}
代码分析
代码首先读入数据组数n,然后通过for(i=1;i<=n;i++){ }结构实现一个n次的计次循环结构,以处理接下来的n组数据。在循环体内,循环一次就处理一组数据,内容包括首先读取金银铜牌三个整数(a,b,c),然后分别累加到三个变量(sum_a,aum_b,aum_c)中。循环结束后,统计奖牌总数,然后输出结果。
②(代码)水仙花数
任务描述
请判断一个数是不是水仙花数。水仙花数是指各个数字立方和等于它本身的三位数。
输入格式:
有多组测试数据,每组测试数据以包含一个整数n(100<=n<1000)输入0表示程序输入结束。
输出格式:
如果n是水仙花数就输出Yes否则输出No
解法1:
#include<stdio.h>
int main()
{
int n, i, a, b, c;
while (1) //每次循环处理一组数据
{
scanf("%d", &n); //读入一组数据
if (n == 0) break; //读入特定值跳出循环
//处理这一组数据
a = n / 100; //提取百位数字
b = n % 100 / 10; //提取十位数字
c = n % 10; //提取个位数字
if (n == a * a * a + b * b * b + c * c * c) //满足条件
{
printf("Yes\n");
}
else
{
printf("No\n");
}
}
return 0;
}
水仙花数的判别我们已经在前一章的“水中仙子”任务中掌握了,基本思想是抽取变量n的百位数、十位数和个位数,赋值给变量“a,b,c”,然后再判断各位数字立方和是否与n相等。和任务1的解法2一样,这里我们也可以设计一种循环结构,在循环入口处(while后的括号内)完成“输入一组数据n”和“判断n是否为0”两件事。
解法2:
#include<stdio.h>
int main()
{
int n, i;
int a, b, c;
while (scanf("%d", &n), n != 0) //读入整数n,遇0停止循环
{
a = n / 100; //提取百位数字
b = n % 100 / 10; //提取十位数字
c = n % 10; //提取个位数字
if (n == a * a * a + b * b * b + c * c * c) //满足水仙花数条件
{
printf("Yes\n");
}
else
{
printf("No\n");
}
}
return 0;
}
③(代码)输出数字字符的和
任务描述
编程输入一串字符(以#字符结束),输出这串字符中所有数字字符的和。
输入样例:
ABC123DE4FG#
输出样例:
10
#include <stdio.h>
int main()
{
int r = 0;
char x;
do
{
scanf("%c", &x);
if (x >= '0' && x <= '9')
{
r += (x - 48); //0的ASCIIs是48
}
} while (x != '#');
printf("%d", r);
return 0;
}