原题地址:[USACO1.5] 回文质数 Prime Palindromes - 洛谷
原题
题目描述
因为 151151 既是一个质数又是一个回文数(从左到右和从右到左是看一样的),所以 151151 是回文质数。
写一个程序来找出范围 [a,b](5≤a<b≤100,000,000)[a,b](5≤a<b≤100,000,000)(一亿)间的所有回文质数。
输入格式
第一行输入两个正整数 aa 和 bb。
输出格式
输出一个回文质数的列表,一行一个。
输入输出样例
输入 #1
5 500
输出 #1
5 7 11 101 131 151 181 191 313 353 373 383
说明/提示
Hint 1: Generate the palindromes and see if they are prime.
提示 1: 找出所有的回文数再判断它们是不是质数(素数).
Hint 2: Generate palindromes by combining digits properly. You might need more than one of the loops like below.
提示 2: 要产生正确的回文数,你可能需要几个像下面这样的循环。
题目翻译来自NOCOW。
USACO Training Section 1.5
产生长度为 55 的回文数:
for (d1 = 1; d1 <= 9; d1+=2) { // 只有奇数才会是素数 for (d2 = 0; d2 <= 9; d2++) { for (d3 = 0; d3 <= 9; d3++) { palindrome = 10000*d1 + 1000*d2 +100*d3 + 10*d2 + d1;//(处理回文数...) } } }
这道题虽然是让你写循环,但是由于数据量大,采取最简单的循环写法会超时,所以需要对于解题方法进行优化。
在洛谷题解中,我注意到有两个方向的优化,一种是直接采用打表的方式进行逃课,另一种则是减少需要讨论的数据量以及减少循环来达到减少时间。
打表法
打表法的介绍请看我之前的文章:关于打表法-CSDN博客
打表法某种意义上也算是空间换时间了,在本地进行循环查找显然不需要考虑是否超时的问题。
因此在本地运行的代码只要正确且能跑出结果就可以。
这是我第一版没有优化过的代码:
#include<iostream>
using namespace std;
int judge1(int n)
{
if(n==2||n==3)
return 1;
for(int i=2;i*i<=n;i++)
{
if(n%i==0)
return 0;
}
return 1;
}
int judge2(int n)
{
if(n<10)
{
if(judge1(n))
return 1;
else
return 0;
}
else if(n>=10&&n<100)
{
if(!judge1(n))
return 0;
for(int i=1;i<=9;i+=2)
{
if(n==10*i+i)
return 1;
}
return 0;
}
else if(n>=100&&n<1000)
{
if(!judge1(n))
return 0;
for(int i=1;i<=9;i+=2)
{
for(int s=0;s<=9;s++)
{
if(n==100*i+10*s+i)
return 1;
}
}
return 0;
}
else if(n>=1000&&n<10000)
{
if(!judge1(n))
return 0;
for(int i=1;i<=9;i+=2)
{
for(int s=0;s<=9;s++)
{
if(n==1000*i+100*s+10*s+i)
return 1;
}
}
return 0;
}
else if(n>=10000&&n<100000)
{
if(!judge1(n))
return 0;
for(int i=1;i<=9;i+=2)
{
for(int s=0;s<=9;s++)
{
for(int q=0;q<=9;q++)
{
if(n==10000*i+1000*s+100*q+10*s+i)
return 1;
}
}
}
return 0;
}
else if(n>=100000&&n<1000000)
{
if(!judge1(n))
return 0;
for(int i=1;i<=9;i+=2)
{
for(int s=0;s<=9;s++)
{
for(int q=0;q<=9;q++)
{
if(n==100000*i+10000*s+1000*q+100*q+10*s+i)
return 1;
}
}
}
return 0;
}
else if(n>=1000000&&n<10000000)
{
if(!judge1(n))
return 0;
for(int i=1;i<=9;i+=2)
{
for(int s=0;s<=9;s++)
{
for(int q=0;q<=9;q++)
{
for(int w=0;w<=9;w++)
{
if(n==1000000*i+100000*s+10000*q+1000*w+100*q+10*s+i)
return 1;
}
}
}
}
return 0;
}
else if(n>=10000000&&n<=100000000)
{
if(!judge1(n))
return 0;
for(int i=1;i<=9;i+=2)
{
for(int s=0;s<=9;s++)
{
for(int q=0;q<=9;q++)
{
for(int w=0;w<=9;w++)
{
if(n==10000000*i+1000000*s+100000*q+10000*w+1000*w+100*q+10*s+i)
return 1;
}
}
}
}
return 0;
}
}
int main()
{
int a,b;
cin>>a>>b;
for(int i=a;i<=b;i++)
{
if(judge2(i))
cout<<i<<endl;
}
}
在这个代码中,我先判断了是否为质数, 再将需要检验的数与对应位长的回文数对应。虽然看起来长,但还是比较直观的,其中包含很多重复的代码,已经可以说是很无脑了。但是用这个代码在本地可以在短时间内跑出结果,如果采用打表法,已经可以满足需求了。
思路优化
这道题涉及到了两类特殊数字:质数和回文数。
对于质数,我们都知道除了2以外,其余偶数都必定不为质数,我们需要讨论的数就少了一半。
对于回文数,有一个我们不是很常见的特殊性质:偶数位回文数除11外都不是质数。具体证明过程,请阅读我之前的文章:关于偶数位回文数只有11是质数的证明-CSDN博客。这样我们需要讨论的数又少了一半。
此外,对于judge2()函数,在判断是否为回文数时可以直接根据对应位的数是否相等来判断,而不是进行大段的,甚至是三重的循环。
在改进之下,时间确实被缩短了,但还是TLE,看来通过循环逐一判断每个数的做法有问题,需要在范围内构造回文数,然后判断是否为质数。(到这一步,就是重写了,悲)
AC代码:
#include <iostream>
using namespace std;
int judge(int n)
{
for (int i = 2; i * i <= n; i++)
{
if (n % i == 0)
return 0;
}
return 1;
}
int main()
{
int l, r;
cin >> l >> r;
if (l == 5)
cout << 5 << endl;
if (l <= 7 && r >= 7)
cout << 7 << endl;
if (l <= 11 && r >= 11)
cout << 11 << endl;
int j = 1;
if (l < 1000)
{
for (int i = 1; i <= 9; i++)
{
for (int s = 0; s <= 9; s++)
{
if (100 * i + 10 * s + i < l)
continue;
else if (100 * i + 10 * s + i > r)
{
j = 0;
break;
}
else
{
if (judge(100 * i + 10 * s + i))
cout << 100 * i + 10 * s + i << endl;
}
}
}
}
if (l < 100000 && j)
{
for (int i = 1; i <= 9; i++)
{
for (int s = 0; s <= 9; s++)
{
for (int p = 0; p <= 9; p++)
{
if (10000 * i + 1000 * s + 100 * p + 10 * s + i < l)
continue;
else if (10000 * i + 1000 * s + 100 * p + 10 * s + i > r)
{
j = 0;
break;
}
else
{
if (judge(10000 * i + 1000 * s + 100 * p + 10 * s + i))
cout << 10000 * i + 1000 * s + 100 * p + 10 * s + i << endl;
}
}
}
}
}
if (l < 10000000 && j)
{
for (int i = 1; i <= 9; i++)
{
for (int s = 0; s <= 9; s++)
{
for (int p = 0; p <= 9; p++)
{
for (int t = 0; t <= 9; t++)
{
int num = 1000000 * i + 100000 * s + 10000 * p + 1000 * t + 100 * p + 10 * s + i;
if (num < l)
continue;
else if (num > r)
{
j = 0;
break;
}
else
{
if (judge(num))
cout << num << endl;
}
}
}
}
}
}
}