题目传送门
P1217 [USACO1.5] 回文质数 Prime Palindromes
题目描述
因为 151 151 151 既是一个质数又是一个回文数(从左到右和从右到左是看一样的),所以 151 151 151 是回文质数。
写一个程序来找出范围 [ a , b ] ( 5 ≤ a < b ≤ 100 , 000 , 000 ) [a,b] (5 \le a < b \le 100,000,000) [a,b](5≤a<b≤100,000,000)(一亿)间的所有回文质数。
输入格式
第一行输入两个正整数 a a a 和 b b b。
输出格式
输出一个回文质数的列表,一行一个。
样例输入 #1
5 500
样例输出 #1
5
7
11
101
131
151
181
191
313
353
373
383
提示
提示 1: 找出所有的回文数再判断它们是不是质数(素数).
提示 2: 要产生正确的回文数,你可能需要几个像下面这样的循环。
产生长度为 5 5 5 的回文数:
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;//(处理回文数...)
}
}
}
重要:11的整倍数有一个性质,那就是奇数位上数字之和=偶数位上数字之和。而4、6、8等偶数长度的回文数都满足此性质,都是11的倍数,因此除11之外,任意偶数长度的回文数都不可能为质数
为了减少计算,可以加一些判断条件,例如 (a<=1000 && b>=100) 才有可能存在三位的回文素数,如果给定的数据范围不匹配,可以跳过生成三位的回文数。其他类同。
#include<bits/stdc++.h>
using namespace std;
bool isPrime(int num) {
if(num < 2) return false;
for(int i=2; i*i<=num; i++) {
if(num%i==0) return false;
}
return true;
}
int main() {
int a, b;
cin>>a>>b;
//处理100以内的回文素数:5,7,11
if(a<=5 && b>=5) cout<<5<<endl;
if(a<=7 && b>=7) cout<<7<<endl;
if(a<=11 && b>=11) cout<<11<<endl;
/*
11的整倍数有一个性质,那就是奇数位上数字之和=偶数位上数字之和。
而4、6、8等偶数长度的回文数都满足此性质,都是11的倍数,因此除
11之外,任意偶数长度的回文数都不可能为质数
*/
//处理长度为3
if(a<=1000 && b>=100) {
for (int d1 = 1; d1 <= 9; d1+=2) { //只有奇数才可能是素数
for (int d2 = 0; d2 <= 9; d2++) {
int num = 100*d1 + 10*d2 + d1; //组合回文数
if(num>=a && num<=b && isPrime(num)) cout<<num<<endl;
}
}
}
//处理长度为5
if(a<=100000 && b>=10000) {
for (int d1 = 1; d1 <= 9; d1+=2) { //只有奇数才可能是素数
for (int d2 = 0; d2 <= 9; d2++) {
for (int d3 = 0; d3 <= 9; d3++) {
int num = 10000*d1 + 1000*d2 +100*d3 + 10*d2 + d1; //组合回文数
if(num>=a && num<=b && isPrime(num)) cout<<num<<endl;
}
}
}
}
//处理长度为7
if(a<=10000000 && b>=1000000) {
for (int d1 = 1; d1 <= 9; d1+=2) { // 只有奇数才会是素数
for (int d2 = 0; d2 <= 9; d2++) {
for (int d3 = 0; d3 <= 9; d3++) {
for (int d4 = 0; d4 <= 9; d4++) {
int num = 1000000*d1 + 100000*d2 +10000*d3 + 1000*d4 + 100*d3 + 10*d2 + d1; //组合回文数
if(num>=a && num<=b && isPrime(num)) cout<<num<<endl;
}
}
}
}
}
return 0;
}
补充个一般思路的代码,两个函数用于判断回文和质数,需要注意的是,一定要先进行回文判断,再进行质数判断,因为数量上来看,回文的数量要远小于质数,反过来会超时。另外,右边界需要做一个限定,虽然可能的右边界可以达到一亿,但最大的回文素数只能是7位,右边界限定到9999999,实际上最大的回文质数是9989899,也可以直接以这个数做边界。
#include<bits/stdc++.h>
using namespace std;
bool isPrime(int num){
if(num < 2) return false;
for(int i=2; i*i<=num; i++){
if(num%i==0) return false;
}
return true;
}
bool isPalindrome(int num){
int sum=0, k=num;
while(k){
sum = sum*10+k%10;
k/=10;
}
return sum==num;
}
int main(){
int a, b;
cin>>a>>b;
for(int i=a; i<=min(b, 9989899); i++){
if(isPalindrome(i) && isPrime(i)) cout<<i<<endl;
}
return 0;
}
再补充个埃氏筛素数预打表的方法,先用埃氏筛素数的方法把所有素数标识出来,同样也需要限制一下边界,筛到一亿会超时。埃氏筛素数打好表之后,就可以先判断素数,再判断回文。
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+1;
bool flag[N];
//埃氏筛素数
void func(){
memset(flag, true, sizeof(flag));
flag[0]=flag[1]=false;
for(int i=2; i*i<=N; i++){
if(flag[i]){
for(int j=i*i; j<=N; j=j+i){
flag[j]=false;
}
}
}
}
bool isPalindrome(int num){
int sum=0, k=num;
while(k){
sum = sum*10+k%10;
k/=10;
}
return sum==num;
}
int main(){
func();
int a, b;
cin>>a>>b;
for(int i=a; i<=min(b, 9989899); i++){
if(flag[i] && isPalindrome(i)) cout<<i<<endl;
}
return 0;
}