老规矩,先扯几句淡。寻找回文质数还是蛮有趣的。可以先找回文数再判断是否是质数,也可以先找出质数在判断是否满足回文数性质,这些可以认为是搜索的思路。我们知道,但凡问题要求找出全部解的时候,一般都意味着两种思路,即搜索和构造。而通常情况下,搜索耗时会大很多,甚至空间耗费也要大不少,但是搜索的办法思维不复杂,编程简单。构造则相反,通常时空表现都好些,但是编程复杂很多,作为ACMer,应该不怕这个,极致追求的是效率!效率!嘿嘿~再说构造,构造肯定是要充分利用数据的特性了,如果找到了比较好的特性——因而构造既易于实现也能保证答案的正确性与全面性——构造几乎无一例外是优于搜索的。
pprime和sprime都是关于构造法求全集的好例子。
对于pprime,回文数的性质那是灰常明显滴摆在那里了——质数性质也很明显,不过这个性质没法子让你使用构造法吧——不用构造法子手都痒。
我的大概思路是,设生成位数为len的回文数,若len是奇数,那么从小到大枚举(l+len) /2位的数,然后构造出一个回文数;若len是偶数,那么从小到大枚举len/2位的数,然后复制翻转生成一个回文数(genepalin)。上诉两个过程交替进行就可以从小到大生成回文数了。
一些重要的剪枝是:任意偶数长度的回文数都不可能为质数(除了11),因为它能被11整除,因为11的整倍数有一个性质,那就是奇数位上数字之和等于偶数位上数字之和,一个数,如果是偶数长度回文数,那么同一个数num,必然出现在一次奇数位一次偶数位,所以这个偶数长度回文数可以被11整除。还有一个优化:尾数为5必不是质数,不过我给忘了用了。看速度还是很快的...
/*
ID: fairyroad
PROG: pprime
LANG: C++
*/
#include <fstream>
#include<cmath>
using namespace std;
ifstream fin("pprime.in");
ofstream fout("pprime.out");
size_t t[10] = {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
size_t prime[10000] = {2};
size_t count = 1;
inline size_t length(size_t num){
size_t res = 1;
while(num/10){ ++res; num/=10; }
return res;
}
inline size_t genepalin(size_t half){
size_t lenh = length(half);
size_t res = half * t[lenh];
size_t len = lenh-1;
while(len){
res+=(half/t[len+1])*t[lenh - len];
half%=t[len+1];
--len;
}
return res;
}
inline void gene_prime(){
for(size_t i = 3; i < 10001; ++i){
bool flag = true;
double d = sqrt((double) i);
for (size_t j = 2; j <= d; j++)
if(i%j==0){ flag = false; break;}
if(flag) prime[count++] = i;
}
}
inline bool is_prime(size_t num){
if(num == 1) return false;
for (size_t i = 0; i < count && prime[i] < num ; ++i)
if(num%prime[i] == 0 && num != prime[i])
return false ;
return true;
}
int main(){
size_t a, b;
fin>>a>>b;
gene_prime();
size_t lena = length(a), lenb = length(b);
size_t i, j;
size_t tmp;
for(i = lena; i < lenb+1; ++i){ // generate palindrome with length i
if(i & 0x1u){ // odd
j = t[(i+1)/2];
for(; j < t[(i+1)/2+1]; ++j){
if(!(j/t[length(j)] & 0x1u)) continue;
tmp = genepalin(j);
if(tmp > b) return 0;
if(tmp >= a && is_prime(tmp)) fout<<tmp<<endl;
}
}
else{ // even
if(i == 2) fout<<11<<endl;
}
}
return 0;
}
对于sprime,同样记住构造法的思路,质数的性质也很明显的。那就是首位只能是质数2,3,5,7,其余位只能是1,3,7,9。有了这个发现,程序也简单了,我用的是深搜。速度很理想。构造万岁呀!
/*
ID: fairyroad
PROG: sprime
LANG: C++
*/
#include<fstream>
#include<cmath>
using namespace std;
ifstream fin("sprime.in");
ofstream fout("sprime.out");
int opt[2][4] = {{2, 3, 5, 7}, {1, 3, 7, 9}};
int len;
inline bool is_prime(int num){
if(num < 2) return false;
double d = sqrt(num);
for (int j = 2; j <= d; j++)
if(num%j==0) return false ;
return true;
}
void sprime(int n, int num){
if(n == len){
fout<<num<<endl;
return;
}
for(int i = 0; i < 4; ++i){
int tmp= num * 10 + opt[!!n][i];
if(is_prime(tmp))
sprime(n+1, tmp);
}
}
int main()
{
fin>>len;
sprime(0, 0);
return 0;
}
微博: http://t.sina.com.cn/g7tianyi
del.icio.us: http://delicious.com/fairyroad
豆瓣:http://www.douban.com/people/Jackierasy/
e-mail: jacklinshi1004@gmail.com