第四章 数组和结构
4.2 N 盏灯排成一排,从 1 到 N 依次编号。有 N 个人也从 1 到 N 依次编号。第一个 人(1 号)将灯全部打开,第二个人(2 号)将凡是 2 和 2 的倍数的灯关闭。第三个人(3 号)将凡是 3 和 3 的倍数的灯做相反处理(即将打开的灯关闭,关闭的灯打开),以后的人 都和 3 号一样,将凡是与自己编号相同的灯和是自己编号倍数的灯做相反处理,请问当第 N 个人操作之后,哪几盏灯是点亮的?试编程求解这个问题,N 由键盘输入。
#include<stdio.h> #define MaxN 1000//定义一个大的处理数,暂时定为 1000
main()
{
int Lamp[MaxN+1]={
0};//初始化每盏灯的状态,0 为关闭,1 为打开
int n;//定义灯数量
int i,j;//定义循环变量
printf("请输入灯数量:");
scanf("%d",&n);//输入灯数量
for(i=1;i<=n;i++)
for(j=i;j<=n;j=j+i)
Lamp[j]=1 −Lamp[j];//参考程序分析
for(i=1;i<=n;i++)
if(Lamp[i]==1)//判断灯是否打开
printf(" 第%d 盏灯是亮的\n",i);
}
程序分析:此题需要解决三个问题,一是如何存储多盏灯的状态,这里是用一个数组来 存储,且用数组元素的值为 1、0 分别表示灯的明灭;二是需要对哪些灯进行处理,这里使 用了两重循环,外循环枚举人,内循环枚举灯;三是如何完成对灯的状态的处理,这里用的 是Lamp[j]=1−Lamp[j]来实现Lamp[j]的值由0变1, 由1变0,当然也可以用Lamp[j]=!Lamp[j] 来表示。
4.3 用简单选择法对 10 个整数排序。
#include <stdio.h>
const int n = 10;
int main()
{
int arr[n+1] = {
0};
int k;
for(int i=1; i<=n; ++i)
{
printf("a[%d]",i);
scanf("%d",&a[i]);
}
//关键循环
for(int i=1; i<=n;++i)
{
k = i;
for(int j=i+1; j<=n; ++j) //j=i+1 开始
{
if(a[j] < a[k])
k = j;
}
a[0] = a[i]; //a[0]做为临时变量
a[i] = a[k];
a[k] = a[0];
}
return 0;
}
小结:数组a的下标1到下标n的位置用于存储待排序元素,而下标0的位置并未存储待排序元素, 表面上是多用了一个存储单元,但这样使用也有其好处:一是数据元素的下标与其序号有更 明显的对应关系,用下标 0 对应第 1 个元素,用下标 i 对应第 i+1 个元素不易理解,且容易 错误;二是在进行交换时,不需另外定义变量来作为临时变量,而是直接以 a[0]来作临时变 量。这对这段用于进行简单选择排序的程序的扩展性和可重用性是有利的。因此,在实际使 用中,用数组存储一个序列时,常采用这种方法,将下标 0 的元素空置不用,而用下标 i 对 应第 i 个元素。当然,在定义数组长度时,其长度要比序列的长度大 1。
4.4 所谓幻方,就是一个 n 行 n 列的正方形,当 n 为奇数时,称为奇数阶幻方。共有 n2 个格子,将 1,2,3,…,n2 这些数字放到这些格子里,使其每行的和、每列的和及两 条对角线的和都是一个相同的数。试编程由键盘输入一个奇数 n,输出一个 n 阶幻方。
解题思路:首先我们将主教材的算法提示按步分解。
(1)定义一个数组 a[n]来存储 n 阶幻方,这里 n 为奇数,数组各元素的初值均为 0, 表示该位置上还没有填数;
(2)用一个整型变量 k 来表示当前要赋的数,按题意 k 将开始从 1 到 n2循环,因为 n 阶幻方将有 n2个数;
(3)定义整型变量 i,j 表示要赋值的行号和列标,i 初值为 0,j 初值为 n/2,表示第一 个数要放在第一行中间的那个格子里;
(4)填数字开始,k 开始循环,完成下面的(5)到(10);
(5)先将 k 赋到相应的位置,即将 a[i]赋为 k,再将 i,j 的值分别用整型变量 iold 和 jold 暂时保存,注意是暂时;
(6)将 i 的值减 1,j 的值增 1,表示后一个数放在这个数的右上格;
(7)如果 i<0 且 j<n ,即右上格从上面超出,则 i=n−1,表示后一数放到与右上格同 列的后一行;
(8)如果 i>=0,且 j>=n,即右上格从右面超出,则 j=0,表示后一数放到与右上格同 行的第一列;
(9)如果 i<0 且 j>=n,即右上格既从右面超出又从上面超出,则将后一数放在前一数 的下面,即 i=1,j=n−1;
(10)如果 a[i][j]>0,即右上格已被数字填充,则后一数放在前一数的下面,即 i=iold+1, j=jold。
#include<stdio.h>
const int n=5;//定义待幻方的阶次,按题目要求,应该为奇数,暂定为 5
main()
{
int a[n][n]={
0};//定义数组并初始化,表示该幻方
int k=1;//定义赋值的数,从 1 到 25
int i=0;
int j=n/2;
int iold,jold;//存储旧的行号和列标 int maxk=n*n;//定义循环的结束值
for(k=1;k<=maxk;k++)//对每一个要赋值的数循环
{
a[i][j]=k;
iold=i;
jold=j;
i=i-1; //向上移一行
j=j+