题目描述
? 有一天路飞突发奇想,他有一个猜想,任意一个大于 2 的偶数好像总能写成 2 个质数的和。路飞查了资料,发现这个猜想很早就被一个叫哥德巴赫的人提出来了,称为哥德巴赫猜想。目前还没有证明这个猜想的正确性。路飞告诉你一个整数 n ,让你用这个数去验证。
? 注意 1 不是质数。
输入 输入一个偶数 n(2≤n≤8000000)? 。
输出 输出一个整数表示有多少对 (x,y) 满足 x+y=n(x≤y) 且x,y 均为质数。
样例输入1 10 样例输出1 2 数据规模与限定 时间限制:1 s
内存限制:64 M
直观方案:
#include <stdio.h>
#include <math.h>
int is_prime(int n){
for(int i=2,I=sqrt(n);i<=I;i++){
if(n%i) continue;
return 0;
}
return 1;
}
int main(){
int n,count=0;
scanf("%d",&n);
for(int i=2;i<=n/2;i++){
if(is_prime(i)&&is_prime(n-i)) count++;
}
printf("%d",count);
return 0;
}
对每个数都需要循环判断是否为质数,效率很低,容易超时:
分析
给的测试数据比较大,得从其他角度思考。
方案一:
给定数组primes,每一位初始化为0代表为质数,从i=2开始,当遇到0时,将2 * 2,2 * 3,2 * 4位置标记为1,依次类推,最终得到得primes数组,质数会被标记为0,合数标记为1。比如primes[2]=0代表2为质数,primes[9]=1代表9为合数。如果primes[i]=primes[n-1]=0,代表找到一组质数对,他们的和等于n。 代码如下:
#include <stdio.h>
#include <math.h>
#define MAX 8000000
int primes[MAX]={0};
void mark_primes(int n){
for(int i=2;i<=sqrt(n);i++){
if(!primes[i]){
for(int j=i*i;j<=n;j+=i){
primes[j]=1;
}
}
}
}
int main(){
int n,count=0;
scanf("%d",&n);
mark_primes(n);
for(int i=2;i<=n/2;i++){
if(!primes[i]&&!primes[n-i]) count++;
}
printf("%d",count);
return 0;
}
结果:
方案二
- 可以先利用素数筛,找到n以内的质数,得到primes数组
- 再循环查找,比如输入n为100时,2为质数,那么98是否在primes中(此时可以用二分查找)。
备注: 循环查找时,并不需要遍历整个primes,因为两个数之和如果等于n,那么必有一个小于等于n/2,另一个大于等于n/2。所以可以将primes根据n/2分成较小数部分和较大数部分,循环小数组部分得到质数1,在大数部分寻找质数2(n-质数1),如果能找到质数2,则所求质数对 +1.
代码实现:
#include <stdio.h>
#define MAX 8000000
int primes[MAX]={0};
void get_primes(int n){
for(int i=2;i<=n;i++){
if(primes[i]==0) primes[++primes[0]]=i;
for(int j=1;i*primes[j]<=n;j++){
primes[i*primes[j]]=1;
if(i%primes[j]==0) break;
}
}
}
int bin_search(int *nums,int target,int len){
int left=0,right=len-1,mid=0;
while(left<=right){
mid=left+((right-left)>>1);
if(nums[mid]==target) return mid;
if(nums[mid]>target) right=mid-1;
else left=mid+1;
}
return -1;
}
int bin_search_right(int *nums,int target,int len){
int left=0,right=len,mid=0;
while(left<right){
mid=left+((right-left)>>1);
if(nums[mid]>target) right=mid;
else left=mid+1;
}
return left-1;
}
int main(){
int n,count=0;
scanf("%d",&n);
get_primes(n);
int flag=bin_search_right(&primes[1],n/2,primes[0])+1;
for(int i=1;i<=flag;i++){
if(~bin_search(&primes[flag],n-primes[i],primes[0]-flag+1)) count++;
}
printf("%d",count);
return 0;
}
结果:
小结
可以看到方案2的代码量要大于方案1,切运用到了二分查找,素数筛的方法。但是内存和时间都略优于方案1.
|