NOI.1.13.11 回文素数 (C++

NOI.1.13.11 回文素数

NOI.1.13.11 回文素数 (C++)

全局题号2248,题目原址:http://noi.openjudge.cn/ch0113/11/
题为输入n,统计并输出所有回文素数的n位数,根据实际情况修改以满足题意
这道题可以取到9位数,导致我一开始用筛选质数表记录,结果空间limited,改为用较高效的素数判断法也超时。看了一大哥帖子思路后瞬间点醒(那帖子找不到链接了道歉)(找到了在后面) 。同时判断回文和素数,老老实实枚举下去容易超时,要缩小解集快速进行某一步(要么耍流氓直接用已用质数表,要么生成出n位的回文数不再判断回文)。素数判断用到了数论的知识略过了很多解空间,同样回文素数后来debug发现了n大于2的偶数位下存在的规律。代码如下,一些解释看后头

#include<iostream>
#include<vector>
using namespace std;

bool isPrime(int n){ //素数存在定理,大于3的素数必处于6的倍数两端
    if(n<=3)return n>1;
    if(n%6!=1&&n%6!=5)return false; //不为6倍数附近则不是素数
    for(int i = 5 ; i*i<=n;i+=6){ //以6的倍数递增,收敛在sqrt n
        if(n%i==0||n%(i+2)==0)return false;
    }
    return true;
}

void generatePal(vector<int>&table, int x, int y,int N){  //将x~y之间的数倒序加入table
    if(N>2&&N%2==0)return ; //跳过所有大于2的偶数次位,必为11的倍数,数量为0
    int f,k;
    f = N%2==1?10:1; //f 控制奇偶情况的不同处理方式
    k = N/2;   //k指明乘几次10,以给后续留出位置
    for(int i = x ;i<=y;i++){ //x~y之间的数的中心对称
        int s = i ,m = i/f, t = 0 ; //s为动态结果串,m为余数提取串
        for(int j = 0; j<k;j++){
            t = m%10+t*10; //余数积累反转
            m/=10;
            s*=10;
        }
        s+=t;   //余数提取加入结果串,入队
        table.push_back(s);
    }
}
int main(){
    int res=0, N,n;cin>>N;
    n = (N+1)/2; // n为N的折半位数
    int X = 1, Y = 0; //X,Y经处理后变为半N位数下的取数范围 [ X, Y ]
    while(n>0){
        X = 10*X;
        Y = 10*Y+9;
        n--;
    }
    X/=10;
    vector<int> Res, tab;
    generatePal(tab,X,Y,N);  //生成N位的回文数组
    for(int i = 0;i<tab.size();i++){
        if(isPrime(tab[i])){  //为素数,统计,入队
            res++;
            Res.push_back(tab[i]);
        }
    }
    //结果输出
    cout<<res<<endl;
    for(int i = 0;i<Res.size();i++){
        cout<<Res[i]<<' ';
    }
    return 0;
}

查看5位数的回文串时,数字关于第3位开始中心对称,如102->10201,这么看来按3位数100开始穷举到999进行中心对称就可以列出5位数的所有回文数(从10000~99999的9w次回文判断到从3位数对称9伯次是很大的飞跃!)
在这里插入图片描述
同样的6位数是前3位的中心对称,如102->102201。位数奇偶会影响一点生成回文串的操作,代码中我用f来进行控制,草稿上演算一下其实就是中间那位要不要取余提取。
在这里插入图片描述
最后调试发现n=4,6,8,res都是0,起初不敢相信4位数里竟然没有回文素数,几番查找,回想起当初知道素数分布规律时的震撼,应该可能大概是同一种方法吧。拆了拆试试,果不其然,这里粗略证明。任意4位数可看作abba,按十进制拆开就为1000a+100b+10b+1a,同类项合并后为1001a+110b,于是可提取11*(91a+10b),即4位数回文串均是11倍数。同理6,8位也能提出11倍来。代码中就可以跳过了大于2的偶数位回文数生成了!
至于素数的分布,就是大于等于5以上的数字都能写成6n-1,6n,6n+1,6n+2,6n+3,6n+4,6(n+1)-1的形式,其中6n,6n+2,6n+3,6n+4明显能提出公因式2,3。即大于等于5以上的数若是素数必分布在6倍数附近(第一次知道的时候好塔玛兴奋)素数判断就按6倍数周围跳着走啦。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值