Advertisement

全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之一维数组(应用技巧)

阅读量:

二、一维数组应用技巧2:打标记

实战训练1—开关灯

问题描述:

有 M 人从 1 到 M 按顺序编号参与一项游戏活动。共有 K 盏从 1 到 K 编号依次排列并亮着(其中 M 和 K 均为正整数,并且满足 M≤K≤5000)。系统会对该 K 盏灯光进行一系列的操作——熄灭与点亮动作。初始状态下所有灯均处于点亮状态;第一个参与者(编号为 1 的人)会将其余所有开关位置;第二个参与者(编号为 2 的人)会对所有标号是其倍数位置上的灯光进行反向操作;第三个参与者(3 号人)也会执行同样的反向操作;以此类推直到最后一位参与者完成全部操作……经过第 M 位参与者完成全部操作后,请列出此时仍处于关闭状态的所有灯具标号,并按照从小到大的顺序用逗号分隔开它们的所有数字部分

输入格式:

输入共一行,包含两个正整数K和M,以单个空格隔开。

输出格式:

输出共一行,顺次输出关闭的灯的编号,其间用逗号间隔。

输入输出样例:

输入样例1 输出样例1
10 10 1,4,9
输入样例2 输出样例2
20 30 1,4,9,16

问题分析:

根据题目要求, 可以运用标记法的思想来解决此问题. 首先创建一个布尔类型的数组, 其中每个元素表示对应的灯的状态: 0代表灯处于点亮状态, 1代表灯处于熄灭状态. 数组初始化时所有元素均为0. 这里需要注意的是由于灯的编号从1开始至k, 所以数组元素从索引1开始赋初值. 接下来模拟开关操作的过程: 经过m次操作后, 每次操作都将所有编号为其当前循环变量倍数的灯切换状态. 为了实现这一过程, 可以使用双层循环结构: 外层循环控制开关的操作次数, 内层循环遍历所有灯光并判断其是否符合切换条件. 最后统计熄灭状态的所有灯光并记录其信息. 在输出结果时需要注意除了最后一盏灯的状态不需要单独列出外, 其余灯光的状态之间需要用逗号分隔开. 具体实现代码如下所示:

复制代码
 #include<bits/stdc++.h>

    
 using namespace std;
    
 int main(){
    
 	const int MAXLEN =5005;//定义常变量的值为5005 
    
 	int k,m;//定义灯的数量变量k和操作数变量m 
    
 	scanf("%d%d",&k,&m);//输入k和m的值 
    
 	bool a[MAXLEN];//定义长度为MAXLEN的布尔类型数组a 
    
 	memset(a+1,0,k*sizeof(bool));//将1到k的初始值设置为0,表示所有的灯都开着,1表示灯关闭 
    
 	int sum=0;//记录关闭灯的数量和 
    
 	for(int i=1;i<=m;i++){//进行m次操作 
    
 		for(int j=1;j<=k;j++){//对于每次操作,依据人的编号,对编号的倍数执行相关的操作 
    
 			if(j%i==0){//查看当前灯j是否为编号i的整数倍 
    
 				a[j]=!a[j];//对灯执行相反操作 
    
 			}
    
 		}
    
 	}
    
 	for(int j=1;j<=k;j++){//依次遍历k盏灯 
    
 		if(a[j]){//灯如果是熄灭的状态 
    
 			sum++;//数量加1 
    
 		}
    
 	}
    
 	//在输出时,灯编号需要用逗号隔开,最后一盏灯不需要逗号,所以使用一个临时变量tmpsum来记录输出了多少盏关闭的灯 
    
 	int tmpsum=0;
    
 	for(int j=1;j<=k;j++){
    
 		if(a[j]){//灯关闭,则输出该盏灯 
    
 			tmpsum++;
    
 			printf("%d",j);//输出灯编号 
    
 			if(tmpsum<sum){//如果没有达到sum,则不是最后一盏关闭的灯,输出逗号 
    
 				printf(",");
    
 			}
    
 		}
    
 	}
    
 	return 0;
    
 } 

三、循环应用技巧3:巧用数组下标,实现计数功能

实战训练2—数字统计

问题描述:

在l到r之间的所有整数中,请问各位数字在整个范围内各自出现的总次数是多少?举个例子,在区间[...]中的整数如...统计结果表明:“0”总共出现了一次,“9”的情况也是如此……

输入格式:

输入共一行,包含两个整数M和N,并用空格分隔开来。

输出格式:

输出共一行,包含十个整数并用空格分开,分别表示数码 0,1,2,… ,9 在序列中出现的次数。

输入输出样例:

输入样例1 输出样例1
101 120 11 32 3 2 2 2 2 2 2 2
输入样例2 输出样例2
219 225 1 2 14 1 1 1 0 0 0 1

问题分析:

按照题目要求,在[l,r]区间内依次提取每个整数值的所有位数,并通过数组记录0至9各数字出现的频率。具体操作方法是:初始化一个长度为10的数组,并将其中所有元素设置为零;然后利用循环变量i遍历该区间内的每一个整数值;每次循环时将当前整数值赋值给临时变量tmp;接着对tmp执行各位分离操作;最后将tmph各位上的数字对应到目标数组中的相应位置并累加计数值。值得注意的是,在程序运行过程中不允许直接对循环索引变量i执行各位分解操作;因为这会使得i失去其作为区间控制变量的作用而导致程序运行错误;因此在处理数据时必须先提取出待分解的具体数值存放在临时变量tmp中

复制代码
 #include<bits/stdc++.h>

    
 using namespace std;
    
 int main(){
    
     int l,r,a[10];//定义整数的取值范围变量l和r,其次定义数字出现的数组a,长度为10的整型数组 
    
     memset(a,0,sizeof(a));//起始时,所有数字出现的次数为0,为数组元素全部赋值为0 
    
     scanf("%d%d",&l,&r);//输入l和r 
    
     for(int i=l;i<=r;i++){//依次列举l和r范围内的所有整数 
    
     int tmp=i;//将整数i赋值给临时变量tmp 
    
     while(tmp){//tmp不为0 
    
         a[tmp%10]++;//分离最后一个数位,并将该数位出现的次数加1 
    
         tmp=tmp/10;//移除最后一个数位 
    
     }
    
     }
    
     for(int i=0;i<10;i++){//依次输出每个数字出现的次数 
    
     printf("%d ",a[i]);
    
     }
    
     return 0;
    
 }

全部评论 (0)

还没有任何评论哟~