原题
https://hihocoder.com/contest/hiho251
描述
哥德巴赫猜想认为“每一个大于2的偶数,都能表示成两个质数之和”。
给定一个大于2的偶数N,你能找到两个质数P和Q满足P<=Q并且P+Q=N吗?
输入
一个偶数N(4 <= N <= 1000000)
输出
输出P和Q。如果有多组解,输出P最小的一组。
我的答案
题目感觉很简单,就是循环遍历小于等于N/2的质数P,若是Q也为质数,则(P,Q)满足要求。于是,我觉得主要就是如何判断质数,这个也很简单,就是看2到根号n之间有没有可以整除n的数。
#include <iostream>
#include <cmath>
// 判断n是否是质数
int if_prime(int n) {
for (int i = 2; i < sqrt(n); i++) {
if (n%i==0) {
return 0;
}
}
return 1;
}
int main(int argc, char** argv) {
int n;
scanf("%d", &n);
for (int i = 2; i <= n/2; i++) {
if (if_prime(i)) {
if (if_prime(n-i)) {
printf("%d %d\n", i, n-i);
return 0;
}
}
}
return 0;
}
解析与思考
解析
由于题目N的范围最大只有1000000,所以我们可以筛出1000000以内的所有质数,并且保存在哈希表里备查。
然后从小到大枚举质数P,检查N-P是不是在哈希表里即可。
思考
按我这样做,最坏的情况下,时间复杂度会超过O(n),如果筛选[1,N]间质数时间复杂度O(n)级那也是极好的。感觉都用不着哈希表,直接用N个元素的数组存0/1表示是否是质数即可
质数筛法
1.Eratosthenes筛法
若一个数是质数,则其倍数是合数。
建立一个布尔类型的数组isPrime,初始化都为true。从2开始枚举,当找到一个isPrime[p]仍然为true时,可以确定p一定是一个质数。接着再将N以内所有p的倍数全部设定为isPrime[p*i]=false。
但是很多合数都被查询不止一次,时间复杂度上O(nlogn)。
PS.对于埃氏筛法时间复杂度的计算,可以阅读 Eratosthenes筛法(埃式筛法)时间复杂度分析
2.Eular质数筛法
改进之后,只利用k的最小质因子去计算一次k。枚举的时候,我们只枚举i的质数倍。
此外,在从小到大依次枚举质数p来计算i的倍数时,我们还需要检查i是否能够被p整除。若i能够被p整除,则停止枚举。
#include <iostream>
#include <vector>
#include <stdlib.h>
using namespace std;
//Eular质数筛法 [1, n]中质数
int main(int argc, char** argv) {
int n, i;
scanf("%d", &n);
bool* isprime = (bool*)malloc(sizeof(bool)*n);
isprime[0] = false;
int primecount = 0;
vector<int> primelist;
for (i = 2; i <= n; i++) {
isprime[i-1] = true;
}
for (i = 2; i <= n; i++) {
if (isprime[i-1] == true) {
primecount++;
primelist.push_back(i);
}
for (int j=0; j < primecount; j++) {
if (i*primelist[j] > n) {
break;
}
else {
isprime[i*primelist[j]-1] = false;
}
if(i % primelist[j] == 0){
break;
}
}
}
printf("%d\n", primecount);
return 0;
}