数据结构与算法35-孪生素数对

Description
如果n和n+2都是素数,我们称其为孪生素数,比如3和5,5和7都是孪生素数。 给你一个区间[a,b],请问此区间有多少对孪生素数?(计数规则:n和n+2都是素数,且n,n+2都在区间[a,b]内)

Input
第一行是一个整数K(K≤ 10000),表示样例的个数。 以后每行一个样例,为两个整数,a和b,1≤a≤b≤5000000。

Output
每行输出一个样例的结果。

Sample Input

6
1 3
4 7
1 10
1 100
1 1000
1 5000000

Sample Output

0
1
2
8
35
32463

参考程序

#include <stdio.h>
#include <math.h>
#define LEN 5000000

int pair_prime[LEN+5]={0};
int amount[LEN+5]={0};

int Judge(int n)
{
	int i;
	if(n==1 || n==0)
	{
		return 0;//not a prime
	}
	else if(n==2)
	{
		return 1;
	}
	else
	{
		for(i=2;i<=(int)sqrt(n);i++)
		{
			if(n%i==0)
			{
				return 0;
			}
		}
		return 1;
	}
}

int main()
{
	int i;
	pair_prime[1]=0;
	pair_prime[2]=0;
	pair_prime[3]=1;
	pair_prime[5]=1;
	
	for(i=6;i<=LEN;i+=6)
	{
		if(Judge(i-1)&&Judge(i+1))
		{
			pair_prime[i-1]=1;
			pair_prime[i+1]=1;
		}
	}
	int cnt=0;
	for(i=5;i<=LEN;i++)
	{
		if(pair_prime[i]&&pair_prime[i-2])
		{
			amount[i]=++cnt;
		}
		else
		{
			amount[i]=amount[i-1];
		}
	}
	int K,a,b;
	scanf("%d",&K);
	while(K--)
	{
		scanf("%d%d",&a,&b);
		if(Judge(a-1)&&Judge(a+1))
		{
			printf("%d\n",amount[b]-amount[a]-1);
		}
		else
		{
			printf("%d\n",amount[b]-amount[a]);
		}
	}
	return 0;
}

解析:本题的难度在于程序时间性能的优化上,解决本题需要关于孪生素数的数学性质即除3和5这一对孪生素数外,其他的孪生素数对只有可能出现在6的倍数的两侧,所以在筛选时,只用考虑6,12,18,24,30,36……这些6的倍数的两侧是否都是素数(设6k是6的倍数,考虑排除法:6k+2,6k+4必然是偶数,肯定不是素数;6k+3是3的倍数,也肯定不是素数;这样就剩下6k+5和6k-1了,而6k+5和6k-1是同余的,所以只有6k-1和6k+1有可能是素数)
参考程序中,使用的数据结构说明:

  • pair_prime数组只会考虑在6的倍数两侧即6k-1,6k+1这两个数会不会都是素数,如果都是素数才会将pair_prime[6k-1]、pair_prime[6k+1]置1,其余素数、非素数都置0.在判断这些素数时,还是使用经典算法。

  • amount数组存放孪生素数对个数,即amount[i]表示从1开始到第i结束(前i个)有多少对孪生素数,是不断累积的。这些孪生素数对都满足闭区间[1,i]

其余需要注意的是,题目给的区间是[a,b],而非[1,b],所以需要用amount[b]-amount[a]得出结果,但是在上面的参考程序中,要注意边界条件,即如果一对孪生素数恰好是a-1,a+1,这时amount[a+1]上统计的孪生素数对是把a-1也统计了进来作为一对计数的,所以需要减去。amount[i]的作用是预处理,对于解决多样例输入的问题时,有减少时间开销的作用,就不需要每组输入都从头统计一遍了。

在Dev cpp编译环境下,如果要开百万级数组,不能放在main函数里声明,应声明为全局变量,否则程序无法运行(段错误,非零返回)

本人在做这道题时也是尝试了很多次,参考了网上其他大佬的算法,暴力求解(逐一判断每个素数±2的位置)必然会超时。也让我刷新了对算法题的认识:有些题的难度在于算法设计策略(比如动态规划、回溯法等等),但确实有些难度在于数学思维或数学技巧,这些的确是我们欠缺的。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值