素数筛法是code中很重要的一个点,经常用于各种小技巧和细节处,这里将素数筛法知识点总结下以及对HDU上的三道初级题目进行题解说明。
【知识点补充:素数筛法】:
今天突然看见这篇之前写的博客,是一篇关于素数筛法的博客,因此在这里总结下,算是复习吧!
筛法的方法很简单:在一个范围为n的区间,对与不超过n的每个非负数num,删除2num、3num、4num、5num...倍数的数,遍历完整个区间,那么剩下的数就是素数。很常见也是下面code用到的一种方法是利用一个vis[]数组来进行标记,不是素数标记为1,是则初始化为0,这样整个区间为0的下标i即为素数。核心code:
memset(vis,0,sizeof(vis));
for(int i=2; i<=n; i++) //判断n以内的质数
for(int j=2; j*i<=n; j++)
vis[i*j]=1;
但这样的筛法是有局限性的,尽管还可以进一步进行优化。但意义不大,这段code已经非常高效了。
适用于10^6范围内的素数筛选。那么可以怎样改进?让它再快一点呢。可以缩小判断范围,一是我们不用遍历到n,而只需要到sqrt(n)即可,然后对num的判断也只需要限定其为素数即可。最后在第二层循环的遍历,起点可以从i^2开始,因为i*2,在i=2时已经判断了。综上改进的code如下:
memset(vis,0,sizeof(vis));
for(int i=2;i<=sqrt(n+0.5);i++)
if(!vis[i]){
prime[k++]=i; //记录素数
for(int j=i*i;j<=n;j+=i)
vis[j]=1;
}
当然方法还有很多,感兴趣可以多多探究下......
【题解如下:1239、2136、2138】
#1239:Calling Extraterrestrial Intelligence Again
题目大意:给定m,a,b,要求在范围内求两个质数p,q,满足p/q>=a/b,p*q<=m,并且质数的乘积尽量大。
解题思路:先可以根据数据范围将素数表进打表处理,在输入数据后直接进行判断。判断的时候注意判断范围,防止TLE,详见code。
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1239
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int MAXN = 10001;
int prime[MAXN];
bool isp[MAXN];
int m,a,b,p,q,num=0;
void init_prime(){
memset(prime,0,sizeof(prime));
memset(isp,0,sizeof(isp));
double d=sqrt(10000*1.0);
for(int i=2; i<=d; i++) //判断10000以内的质数
for(int j=2; j*i<10000; j++)
isp[i*j]=1;
for(int i=2;i<MAXN;i++){ //记录质数以及计数
if(!isp[i]){
prime[num]=i;
num++;
}
}
}
int main(){
init_prime(); //计时之前进行打表初始化
while(scanf("%d%d%d",&m,&a,&b)!=EOF && (m || a || b)){
float c=a*1.0/b;
int d,pro,max=0;
for(int i=num-1;i>=0;i--){ //注意循环范围
for(int j=num-1;j>=i;j--){
pro=prime[i]*prime[j];
d=prime[i]*1.0/prime[j];
if(pro<=m && d>=c){ //条件判断
if(pro>max){ //判断是否为最大积
max=pro;
p=prime[i];
q=prime[j];
}
else break;
}
}
}
printf("%d %d\n",p,q);
}
return 0;
}
#2136:Largest prime factor
题目大意:给所有质数编号,其余的数编号为最大质数的序号。任意输入一个数,询问其编号是多少?
解题思路:类似素数打表,将素数以及素数的倍数进行同样的编号。那么素数编号是递增的,而其它数均会被最大素数的编号覆盖。
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=2138
code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e6;
int pri[MAXN],n,k;
void init_prime(){
k=0;
memset(pri,0,sizeof(pri));
for(int i=2;i<=MAXN;i++){
if(!pri[i]){
k++; //编号
for(int j=i;j<=MAXN;j+=i) //对所有质数以及质数的倍数进行编号
pri[j]=k;
}
}
}
int main(){
init_prime();
while(scanf("%d",&n)!=EOF)
printf("%d\n",pri[n]);
return 0;
}
#2138:How many prime numbers
题目大意:给出数据,判断该组数据中有多少个素数?
解题思路:开始以为测试数据量会很大,担心会T,直接打表了,结果超内存了。然后改了下,小数据打表,大数据暴力判断,结果还是不行。最后直接对输入的数据进行输入一个判断一下,然后计数,一下就A了。想了下,时间复杂度为O(N),是可行的!很伤心,其它的很简单,详见code。
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=2136
code:
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int n,num,ans;
int isprime(int m){ //直接判断
int sq=(int)sqrt(m+0.5);
for(int i=2;i<=sq;i++)
if(m%i==0) return 0;
return 1;
}
int main(){
while(scanf("%d",&n)!=EOF){
ans=0;
for(int i=0;i<n;i++){
scanf("%d",&num);
if(isprime(num) && num!=1) ans++;
}
printf("%d\n",ans);
}
return 0;
}