poj Prime Distance(暴力 二次筛法)

此题数据范围灰常大,输入范围高达int的最大值,故素数是不可能全部保存下来的。。借此机会学习了二次筛法,其实就是用n里面的所有合数可以被sqet(n)里面所有质数给筛掉的原理,故只需要先筛出2^16次方以下的素数就可以了,一直用的是最普通的筛法,找时间去学学线性筛法。。。筛完后,看输入进来的a,b,如果b小于2^16那就直接复制原来素数表里的,如果不是,就要通过素数表里的素数来筛了,具体方法是从第一个素数p开始,乘以大于等于a的最小值k,再在标记数组里标记(p*k-a)为1,k++,重复操作直到b为止。这样的好处在于标记数组是从0到b-a的就不会浪费空间,因为范围实在太大开不了2^32次方的数组,在下面找素数的时候第i位置标记是0,说明是素数就可以用i+a来得到素数的值。

还需要正确估计素数的多少,就要用到一个公式n个连续的数,素数的个数大概n/ln(n)。这个公式得出来的值n越大越精确。

AC代码:

#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<ctime>
using namespace std;
#define NMAX 50000
#define ll long long
bool mark[1000010];
int prime[7000],pp[80000];
int pprime()
{
    int i,j,k,m = NMAX;
    for(i = 2; i*i < m; i++)
    {
        for(j = i*i; j < m; j+=i)
            mark[j] = true;
    }
    for(k = 0,i = 2; i < m; i++)
        if(!mark[i]) prime[k++] = i;
    return k;

}

int main()
{
//    freopen("input.txt","r",stdin);
//    freopen("o1.txt","w",stdout);
    int n = pprime();
    int a,b,i,j,k;
    while(~scanf("%d %d",&a,&b))
    {
        int e,fmin,fmax,n2=0;
        memset(mark,0,sizeof(mark));
        if(b <= NMAX)
        {
            for(i = 0; prime[i] < a; i++);
            for(n2 = 0; prime[i] <= b;)
                pp[n2++] = prime[i++];
        }

        else
        {
            for(i = 0; i < n && prime[i]*prime[i] <= b; i++)
            {
                if(prime[i] >= a) k = 2;
                else
                {
                    k = a/prime[i];
                    if(prime[i]%2) k++;
                }
                while(k <= b/prime[i])//这里本来写的是k*prime[i]<b。但是会超int范围就改成除法了
                {
                    mark[k*prime[i]-a] = 1;
                    k++;
                }
            }
            for(i = 0; i <= b-a; i++)
                if(!mark[i]) pp[n2++] = i+a;
        }
        e = n2;
        if(e <= 1)
            printf("There are no adjacent primes.\n");
        else
        {
            int Min=1000000,Max=0;
            for(i = e-1; i > 0; i--)
            {
                int temp = pp[i] - pp[i-1];
                if(temp >= Max)
                {
                    fmax = i-1;
                    Max = temp;
                }
                if(temp <= Min)
                {
                    fmin = i-1;
                    Min = temp;
                }
            }
            printf("%d,%d are closest, %d,%d are most distant.\n",pp[fmin],pp[fmin+1],pp[fmax],pp[fmax+1]);
        }
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值