USACO Prime Palindrome, SuperPrime Rib

老规矩,先扯几句淡。寻找回文质数还是蛮有趣的。可以先找回文数再判断是否是质数,也可以先找出质数在判断是否满足回文数性质,这些可以认为是搜索的思路。我们知道,但凡问题要求找出全部解的时候,一般都意味着两种思路,即搜索和构造。而通常情况下,搜索耗时会大很多,甚至空间耗费也要大不少,但是搜索的办法思维不复杂,编程简单。构造则相反,通常时空表现都好些,但是编程复杂很多,作为ACMer,应该不怕这个,极致追求的是效率!效率!嘿嘿~再说构造,构造肯定是要充分利用数据的特性了,如果找到了比较好的特性——因而构造既易于实现也能保证答案的正确性与全面性——构造几乎无一例外是优于搜索的。

pprimesprime都是关于构造法求全集的好例子。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值