#记录做题过程#
https://www.luogu.com.cn/problem/P1217
这是一道洛谷的普及题,刚学c语言的时候做题经常遇到什么质数,什么回文的题,反正遇到的不少,当时以为这道题也会是那种常见的题,结果当然是几乎全是TLE超时,也是,有关质数和回文的题如果暴力枚举能AC,估计也不会是普及题了。当时写了半天,结果一直TLE就先搁置了,两个多月后的今天终于AC了,想写一篇文章来记录我的做题过程哈,希望大家多多关照。
- 题目描述
因为 151151 既是一个质数又是一个回文数(从左到右和从右到左是看一样的),所以 151151 是回文质数。
写一个程序来找出范围 [a,b](5≤a<b≤100,000,000)(一亿)间的所有回文质数。
-
输入格式
第一行输入两个正整数 a 和 b。 -
输出格式
输出一个回文质数的列表,一行一个。
输入输出样例
输入
5 500
输出
5 7 11 101 131 151 181 191 313 353 373 383
我的做题想法
回文质数,顾名思义,要判断回文数和质数。回文数就是从左往右看和从右往左看都是同一个数字嘛。而质数就是这个数除了1和它本身之外的数还能被其他正整数整除的数(从2开始)。该如何判断?洛谷对这题给了我们一个小小的提示,先判断回文数再去看它是不是质数,回文数少很多,我们从1~100随便想想,回文数就那几个,而质数却不少。而且找回文数更快,把当前的数逆置就知道是不是了,质数可能要枚举很多才知道是不是质数。质数虽然难判但是我们可以通过数学知识去筛出很多不符合条件的数,首先偶数肯定不是质数(2除外),因为可以被2整除,一遇到偶数就赶紧continue,减少枚举次数,减少运行时间,这样一半的数就被排除在外了。接下来先判回文,再判质数。
回文数的判断
数的范围从a到b,一上来先来个if(i%2==0)continue;的筛除偶数,a最小是5,不用管2了,遇到偶数直接pass掉。接着才判断回文。先把i逆置。代码如下:
for (int i = a; i <= b; i++)
{
c = 0;//c初始化为0
int t = i;//先把i赋值给t,后面数的逆置操作会破坏原来的数,先用t存i,而且后面需要用得上i去判断
if (t % 2 == 0)continue;
while (t)
{
d = t % 10;//将原数的个位取出来
c = c * 10 + d;//核心,整数逆置,先取出来的数在高位,后取出来的数在低位,要注意c一定要初
//始化为0
t /= 10;//丢掉原数的个位
}
if (c != i)continue;//如果最终的c和i相同,是回文数,否则continue
质数判断
我觉得质数判断这个地方是TLE得关键,解决了这个地方,可以减少很多次枚举,像前面所说的if(i%2==0)continue; 就是可以有效筛除很多数,接下来的代码中还有一个地方来减少枚举次数,一个数i是不是质数,我们最直接的办法是从x=2~i-1的数一个个试,看这些数能不能整除i,即i%x会不会等于0,等于0那肯定不是质数了,这就可以break了。然而这种方法还是不够优化,其实只要到根号i就行了,你想一个数,比如16,4*4=16,除了4之外,这些数分布在4的两侧,一个小于4的整数s乘一个数t要等于16,这个数t肯定要大于4才行,如果16/s不是正整数,那么在大于4的数中也找不到这样一个数t,因此只要判断到根号i 即可,根号i 要用到sqrt()函数和#include<cmath>的头文件,这也会增加运行时长,建议用x*x<=i去判断。代码:
if (c != i)continue;
else //i和c相等,i是回文数,接下来判是不是质数
{
for (int j = 2; j * j <= c; j++) //用j*j<=c,用sqrt(c)会增加运行时间
{
temp = 1;
if (c % j == 0) { temp = 0; break; }//能被整除,不是质数,赶紧break了,去判下
//一个数了
}
if (temp) printf("%d\n", c);
}
完整代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a, b;
int c = 0, d, temp;
scanf("%d%d", &a, &b);
for (int i = a; i <= b; i++)
{
c = 0;
int t = i;
if (t % 2 == 0)continue;
while (t)
{
d = t % 10;
c = c * 10 + d;
t /= 10;
}
if (c != i)continue;
else
{
for (int j = 2; j * j <= c; j++)
{
temp = 1;
if (c % j == 0) { temp = 0; break; }
}
if (temp)printf("%d\n", c);
}
}
return 0;
}
结束语
终于AC了,但我发现好像和上次相比也没优化多少,可能上一次是暴力枚举,这一次稍微减少了枚举的次数吧。洛谷上的题解中都有说偶数位的回文数(除了11)必然不是质数,可能以前老师讲过但我忘了吧。。。这个结论还是不太懂,但不得不说用上它可以优化将近10倍,上面我自己写的代码用最强的数据5~100000000去测试,用了530毫秒,大概是1s的一半时间,而用上偶数位的回文数(除了11)必然不是质数,最强数据只用了52毫秒跑完,如果数据在强一些,我的写法绝对会再次TLE。。。 害,算法高深莫测,人生如逆旅,我亦是行人。