英雄会(csdn pongo)题解之半质数的个数--2·14情人&元宵节专题

题目详情

质数是大家熟知的概念,我们定义一个半质数的概念:如果一个数恰好是两个质数的乘积(可以相同),则称它为半质数。前几个半质数是 4, 6, 9, 10, 14, 15, 21, 22, 25, 26。我们的问题是,输入两个正整数x<=y,问[x,y]之间有多少个半质数?

输入:x,y

输出:[x,y]之间有多少个半质数。

输入数据范围 1<=x<=y<=2000000。

1.我的做法:

需要明白

(1)两个质数的乘积不等于其他任何两个质数的乘积,因为c=a×b,a,b为质数,那么c的所有因子为1,a,b,a×b,所以c不等于任何其他两个质数的乘积。

也就是说只要两个质数的乘积在[x,y]区间内,那么我们所求的结果就要加1,因为两个质数的乘积不会重复。

(2)某个质数的平方如果大于y,那么这个质数与比他大的质数的乘积肯定大于y。

根据上面两条得出以下做法:

prime保存的是从小到大的质数;

求得y的平方根sy(对下取整),找到小于sy且最接近的质数,赋值给sy,如果不存在这样的质数,返回结果0;

for(sx=2;sx<=sy,sx=下一个质数){

       从第i个质数开始,sx与prime[i]乘积大于等于x;

       到第j个质数为止,sx与prime[j]乘积小于等于y;

       结果+=j-i+1;

}

返回结果


2.我的质数打表方法:

我用两个数组来实现打表:

第一个数组num[2000001]保存的是比本数组下标小且最接近的质数在第二个数组中的下标;

第二个数组prime[]依次保存的是2000000内的质数(从小到大)。

为了便于使用,多分配一个空间,第0个元素不使用,从第一个元素开始。且num[1]=-1,图示如下,打表代码见下面:


这样易于找到比某个数小且最接近的质数,且容易得到这个质数是第几个质数;

如比i小且最接近的质数是prime[num[i]], 这个质数是第num[i]个质数,便于程序计算结果。

当然这里可以用筛法进行优化。

3.C++代码如下:

#include <stdio.h>
#include <iostream>
#include <string>
#include <cmath>
#include <ctime>
using namespace std;
#define MAX 2000000
class Test {
public:
    static int* num;
    static int* prime;
    static bool isPrime(int i){//判断是否是质数
        if(i<2)return false;
        if(2==i)return true;
        int s=(int)sqrt(double(i));
        for(int j=2;j<=s;++j){
            if(0==i%j) return false;
        }
        return true;
    }
    static int comp(int x,int y){
		if(y<=3)return 0;
        int res=0;
		int sx=2;
		int sy=prime[num[(int)sqrt((double)y)]];
        if(sy<=1)return 0;
        while(sx<=sy){ 
			int tmpX=x/sx;
			if(tmpX<=1)tmpX=2;//2跟num数组有关,前两个存储的不是质数数组的索引
			int indexX=num[tmpX]>num[sx]?num[tmpX]:num[sx];
			while(sx*prime[indexX]<x)
				++indexX;
			int tmpY=y/sx;
			if(tmpY>=2)//2跟num数组有关,前两个存储的不是质数数组的索引
				if(num[tmpY]>=indexX)
					res+=num[tmpY]-indexX+1;	
            sx=prime[num[sx]+1];
        }
        return res;
    }
    static int howmany (int   x,int   y){
		//第一次调用函数,进行质数打表
        static bool firstTime=true;
        if(firstTime){
            firstTime=false;
			//为了每次取数组元素时,不进行-1操作,多分配一个空间,不使用第0个
            num=new int[MAX+1];
            prime=new int[150001];
            num[1]=-1;//1
            num[2]=1;//2
            prime[1]=2;
            int pNum=1;
            for(int i=3;i<=MAX;++i)
                if(isPrime(i)){
                    num[i]=++pNum;
                    prime[pNum]=i;
                }else
                    num[i]=pNum;  
        }
        return comp( x,y);
    }
};
int*Test::num;
int*Test::prime;

//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{   
     clock_t t1=clock();
	 cout<<Test::howmany(1,2)<<endl;
	 cout<<Test::howmany(1,30)<<endl;
    cout<<Test::howmany(6,30)<<endl;   
	cout<<Test::howmany(6,2000000)<<endl; 
     cout<<Test::howmany(1,4)<<endl;
    clock_t t2=clock();
    std::cout<<(t2-t1)/(double)CLOCKS_PER_SEC<<std::endl;    
} 
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。

4.时间复杂度为O(√y)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值