问题描述
幸运数是波兰数学家乌拉姆命名的。它采用与生成素数类似的“筛法”生成
。首先从1开始写出自然数1,2,3,4,5,6,....
1 就是第一个幸运数。
我们从2这个数开始。把所有序号能被2整除的项删除,变为:
1 _ 3 _ 5 _ 7 _ 9 ....
把它们缩紧,重新记序,为:
1 3 5 7 9 .... 。这时,3为第2个幸运数,然后把所有能被3整除的序号位置的数删去。注意,是序号位置,不是那个数本身能否被3整除!! 删除的应该是5,11, 17, ...
此时7为第3个幸运数,然后再删去序号位置能被7整除的(19,39,...)
最后剩下的序列类似:
1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75, 79, ...
输入格式
输入两个正整数m n, 用空格分开 (m < n < 1000*1000)
输出格式
程序输出 位于m和n之间的幸运数的个数(不包含m和n)。
样例输入1
1 20
样例输出1
5
样例输入2
30 69
样例输出2
8
解题思路:此题与素数筛法不同的地方在于筛选的是位置下标而不是数值,解题的关键是如何保存每次筛后的数组。
解题思路:此题与素数筛法不同的地方在于筛选的是位置下标而不是数值,解题的关键是如何保存每次筛后的数组。
思路1:记录需要向前压缩的步数,每次筛去数字后向前的压缩的步数+1。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> /*记录每次压缩后的步长*/ const int Max_N = 1000000; int m,n;//输入 int nums[Max_N]; //保存数字 void solve() { //初始化 for( int i=0; i<n; i++ ) { nums[i] = i+1; } int step, key = 2, pos = 1, res = 0; //step:压缩步长 key:当前幸运数 pos:幸运数在nums的下标 res:(m,n)幸运数的和 while( key<n ) { step = 0; for( int i=0; i<n; i++ ) { if( nums[i]!=0 ) { if( (i+1)%key==0 ) //需要被筛去 { nums[i] = 0; //筛去 step++; //压缩步长+1 } else { nums[i-step] = nums[i]; //向前压缩 if( step>0 ) { nums[i] = 0; //如果num[i]向前压缩至i-step 则num[i]清零 } } } else break; //nums[]已经全被压缩 } key = nums[pos++]; //更新幸运数 if( key==0 ) break; //已经没有幸运数了(nums[]后面的数均为0) /*特别注意:要在key更新后立即判断 否则之后可能涉及余0操作 结果出错*/ if( m<key&&key<n ) res++; //更新res } printf("%d ",res); } int main() { scanf("%d%d",&m,&n); solve(); return 0; }
思路2:用一个二维数组交替保存筛去后的数字。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> const int Max_N = 1000000; int m,n; //输入 int nums[Max_N][2]; //用两个数组交替保存筛过之后的数 void solve() { //初始化 for( int i=0; i<n; i++ ){ nums[i][0] = i+1; } int flag = 0,pos = 1,key = 2, length = n; //flag:用于交替操作 length:循环长度(这里没有把筛去的数字变为0) while( key<n ) { //从num[][from]保存至num[][to] int from = flag%2, to = (flag+1)%2; int index = 0; for( int i=0; i<length; i++ ) { if( (i+1)%key!=0 ){ //把不被筛去的数字保存至另一个数组里 from-->to nums[index++][to] = nums[i][from]; } } length = index;// 更新循环长度 key = nums[pos++][to]; if( key==0 ) break; flag++; // 更新flag 交替保存 } //最后记录res 在循环内直接记录res会因为继续交替两个数组而出错 int res = 0 , to = (flag+1)%2; for( int i=0; i<length; i++ ) { if( m<nums[i][to]&&nums[i][to]<n ) res++; } printf("%d ",res); } int main() { scanf("%d%d",&m,&n); solve(); return 0; }