首页 > 技术文章 > 蓝桥杯 试题 历届试题 幸运数 筛法

w-like-code 2020-07-31 22:39 原文

问题描述

幸运数是波兰数学家乌拉姆命名的。它采用与生成素数类似的“筛法”生成

首先从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。
#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\n",res);
} 

int main()
{
    scanf("%d%d",&m,&n);
    
    solve();
    
    return 0;
}
View Code

思路2:用一个二维数组交替保存筛去后的数字。

#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\n",res);
}

int main()
{
    scanf("%d%d",&m,&n);
    
    solve();
    
    return 0;
}
View Code

 

推荐阅读