区间筛法:L~R,先筛出根号R的素数,再用这些素数把L ~ R的合数筛掉,剩下就是素数,适合R大,区间小的素数区间。
题目链接: http://poj.org/problem?id=2689
题意: 给出一个区间 [l, r] 求其中相邻的距离最近和最远的素数对 . 其中 1 <= l < r <= 2,147,483,647, r - l <= 1e6 .
思路: 素数区间筛
要找到 [l, r] 中相邻最近和最远的素数对肯定是需要找出 [l, r] 内所有素数 . 但是无论是直接线性打表还是暴力都处理不了这么大的数据 .
可以先给 sqrt (r ) 内的素数打个表, 再用 sqrt(r ) 内的素数去筛选 [l, r] 内的合数, 然后再遍历一次 [l, r], 记录答案即可 。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int cnt,L,R,X1,Y1,X2,Y2;
const int maxn=1000001;
bool is_prime[maxn];
bool flag[maxn];
int a[maxn];
int b[maxn];
void Prime()
{
/*int n=46340;
cnt=0;
is_prime[1]=0;
for(int i=2;i<=n;i++)
is_prime[i]=1;
for(int i=2;i*i<=n;i++)
{
if(is_prime[i])
{
a[++cnt]=i;
for(int j=i*i;j<=n;j+=i)
{
is_prime[j]=0;
}
}
}
*/
memset(is_prime, 1, sizeof(is_prime));
for (int i = 2; i <= 46340; i++)
if (is_prime[i])
{
a[++cnt] = i;
for (int j = 2; j <= 46340 / i; j++) is_prime[i*j] = false;
}
}
int main ()
{
Prime();
while(scanf("%d%d",&L,&R)!=EOF)
{
int cnt2=0;
//cout << cnt2;
memset(flag, 1, sizeof(flag));
for(int i=1;i<=cnt;i++)
{
for(int j=L/a[i];j<=R/a[i];j++)
{
if(j>1){
//cout << j <<endl;
flag[j*a[i]-L]=0;
//cout << j*a[i]-L<<endl;
}
}
}
if(L==1) flag[0]=0;
for(int j=0;j<=R-L;j++)
{
if(flag[j]==1)
{
b[++cnt2]=L+j;
//if (i == R) break;
//cout <<cnt2 <<endl;
}
}
int minx=2147483647,maxx=-1;
for(int i=1;i<cnt2;i++)
{
int t=b[i+1]-b[i];
if(t>maxx)
{
maxx=t;
Y1=b[i];
Y2=b[i+1];
}
if(t<minx)
{
minx=t;
X1=b[i];
X2=b[i+1];
}
}
//cout << cnt2 <<endl;
if(cnt2<2)
printf("There are no adjacent primes.\n");
else
printf("%d,%d are closest, %d,%d are most distant.\n",X1,X2,Y1,Y2);
}
return 0;
}
坑点:
1.bool定义flag数组,不然容易memset成1出错。
2.int正好是2,147,483,647,for循环到这个数要直接跳出,不能返回循环体+1会溢出int。
或者直接枚举偏移量。