2020.02.23 , 学习素数筛选算法,同时记住了在初始化时常使用memset() , fill() , 前者的速度较快
1007 素数对猜想 (20分)
让我们定义dn为:dn=pn+1−pn,其中pi是第i个素数。显然有d1=1,且对于n>1有dn是偶数。“素数对猜想”认为“存在无穷多对相邻且差为2的素数”。
现给定任意正整数N(<105),请计算不超过N的满足猜想的素数对的个数。
输入格式:
输入在一行给出正整数N。
输出格式:
在一行中输出不超过N的满足猜想的素数对的个数。
输入样例:
20
输出样例:
4
这个题是乙级的一道题目,题意非常的易懂,就是计算给定正整数输入范围内相邻且差为2的素数对的对数,思路当然是先判断得出范围内的哪些数是素数,然后对素数序列进行判断,寻找符合条件的素数对并计数
法一
这些头文件不是全部都用上的,只是做题使用的模板,涵盖了大部分的STL。这种方法使用最普通的求素数的方法,用一个函数判断是否为素数,得出序列后然后进行计算。这里要关注的是这种求素数的方法的时间复杂度是O(n*sqrt(n)),虽说已经可以解出来这道题,但是耗时用了18ms,还是可以进行优化,减少耗时
#include<iostream> //输入输出流头文件
#include<stdio.h> //标准输入输出
#include<stdlib.h>
#include<math.h> //数学函数
#include<string.h> //C语言字符数组的字符串
#include<algorithm> //C++标准模板库的函数
#include<map> //map映射容器
#include<unordered_map> //无序的map映射容器
#include<vector> //变长数组容器
#include<queue> //队列
#include<stack> //栈
#include<string> //C++string类
#include<set> //set集合
#define SIZE 500005
using namespace std; //标准命名空间
//可以加入全局变量或者其他函数
bool isPrime(int n) {
if (n <= 3) {
return n > 1;
}
//判断一个数能否被小于sqrt(n)的数整除
int sqrtnum = (int)sqrt(n);
for (int i = 2; i <= sqrtnum; i++) {
if(n % i == 0) {
return false;
}
}
return true;
}
int main(){ //主函数
#ifdef ONLINE_JUDGE //如果有oj系统(在线判定),则忽略文件读入,否则使用文件作为标准输入
#else
freopen("1.txt", "r", stdin); //从1.txt输入数据
#endif
int N;
cin >> N;
bool num[N];
fill(num , num + N , false);
for(int i = 2 ; i <= N ; i++){
if(isPrime(i)){
num[i] = true;
}
}
// for(int k = 0;k <= N ;k++){
// cout << num[k] << " ";
// }
// cout << endl;
int counter = 0;
// //打印观察素数标记
// for(int i = 2;i <= N ;i++){
// if(num[i] != false){
// cout << i << " ";
// }
// }
// cout << endl;
for(int i = 2 ; i <= N ; i++){
int c = 0 , d = 0;
if(num[i] != 0){
c = i;
}
for(int j = i + 1 ; j <= N ; j++){
if(num[j] != 0){
d = j;
break;
}
}
if(d - c == 2){
//cout << c << " " << d << endl;
counter++;
}
}
cout << counter << endl;
return 0; //返回0,如果不返回0,PAT会报错
}
法二(素数筛选)
第二种方法使用素数筛选算法,开一个大的bool型数组大小就是n+1就可以了.先把所有的下标初始化为true,基本思想是先假设从2到N的全部数为素数,从2开始扫描,对于数i,可以得知k * i(k = 2 , 3 , 4 ······ k)都不会是素数,这些数至少都有i这个因子 , 每个合数必然有一个质因子,所以可以只用质数来筛选 , 与此同时j的初始条件可以写成 j = i * i , 因为若j 存在某个质因子 , 也会被筛选掉,利用这个规律逐步进行剔除 , 最后总的时间复杂度为 N / 2 + N / 3 + N / 4 + ······ N / N = O(N * log N) , 可以很明显看到耗时减少,比第一种方法筛选的效率更加高
bool isPrime[N + 1];
//这里的初始化也可以用memset(isPrime , true , sizeof(isPrime))
fill(isPrime , isPrime + N + 1 , true);
for(int i = 2 ; i < N ; i++){
if(isPrime[i]){
if(i > N / i){//防止后面i*i溢出
continue;
}
for(int j = i * i ; j <= N ; j += i){
isPrime[j] = false;
}
}
}
完整的题目解法如下
#include<iostream> //输入输出流头文件
#include<stdio.h> //标准输入输出
#include<stdlib.h>
#include<math.h> //数学函数
#include<string.h> //C语言字符数组的字符串
#include<algorithm> //C++标准模板库的函数
#include<map> //map映射容器
#include<unordered_map> //无序的map映射容器
#include<vector> //变长数组容器
#include<queue> //队列
#include<stack> //栈
#include<string> //C++string类
#include<set> //set集合
using namespace std; //标准命名空间
//可以加入全局变量或者其他函数
int main(){ //主函数
#ifdef ONLINE_JUDGE //如果有oj系统(在线判定),则忽略文件读入,否则使用文件作为标准输入
#else
freopen("1.txt", "r", stdin); //从1.txt输入数据
#endif
int N,i,j;
cin >> N;
bool isPrime[N + 1];
//这里的初始化也可以用memset(isPrime , true , sizeof(isPrime))
fill(isPrime , isPrime + N + 1 , true);
for(int i = 2 ; i < N ; i++){
if(isPrime[i]){
if(i > N / i){//防止后面i*i溢出
continue;
}
for(int j = i * i ; j <= N ; j += i){
isPrime[j] = false;
}
}
}
int counter = 0;
for(int i = 2 ; i <= N ; i++){
int c = 0 , d = 0;
if(isPrime[i]){
c = i;
}
for(int j = i + 1 ; j <= N ; j++){
if(isPrime[j]){
d = j;
break;
}
}
if(d - c == 2){
//cout << c << " " << d << endl;
counter++;
}
}
cout << counter << endl;
return 0;
}