老規矩,先扯幾句淡。尋找回文質數還是蠻有趣的。可以先找回文數再判斷是否是質數,也可以先找出質數在判斷是否滿足迴文數性質,這些可以認爲是搜索的思路。我們知道,但凡問題要求找出全部解的時候,一般都意味着兩種思路,即搜索和構造。而通常情況下,搜索耗時會大很多,甚至空間耗費也要大不少,但是搜索的辦法思維不復雜,編程簡單。構造則相反,通常時空表現都好些,但是編程複雜很多,作爲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: [email protected]