洛谷 P1217. 回文质数---关于暴力枚举如何优化的问题
[USACO1.5] 回文质数 Prime Palindromes
题目描述
因为 \(151\) 既是一个质数又是一个回文数(从左到右和从右到左是看一样的),所以 \(151\) 是回文质数。
写一个程序来找出范围 \([a,b] (5 \le a < b \le 100,000,000)\)(一亿)间的所有回文质数。
输入格式
第一行输入两个正整数 \(a\) 和 \(b\)。
输出格式
输出一个回文质数的列表,一行一个。
样例 #1
样例输入 #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
产生长度为 \(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;//(处理回文数...)
}
}
}
题解
这道题我做的时候只能通过前九个数据点 最后一组会TLE 本题学习了如何去解决TLE的方法
1. 多加限制条件
2. 先判断容易判断正误的 比如本题回文质数比质数更少 我们就先判断他是不是回文质数 再判断他是不是质数
3. 我们将不同的判断部分分成几个check函数 进行先后check
4. 比我自己写的快一倍左右
来自 https://www.luogu.com.cn/article/eksn23nt
#include <bits/stdc++.h>
using namespace std;
int a, b;
bool check1(int x) //判断当前数的位数是否是偶数位 除了11 其余偶数位回文数都不是质数 所以我们从4位开始判断
{
//由于本题数据范围只到1e8 所以我们只用判断偶数位 4 和 6
if ((1000 <= x && x <= 9999) || (1000000 <= x && x <= 999999)) return false;
return true;
}
bool check2(int x) //判断是不是回文数
{
int arr[20], flag = 1; //arr[]存x第flag位上的值 从低位向高位存储
//将x每一位按顺序存在arr数组里 和我的std::string s = std::to_string(x)类似
while (x > 0)
{
arr[flag] = x % 10;
x /= 10; //等同于删去当前位 向高位移动
flag ++ ;
}
for (int i = 1; i <= flag / 2; i ++ ) //由于上面循环内flag最后++了 所以偶数奇数均适用 比如x有3位就会判断到第2位
{
if (arr[i] != arr[flag - i]) return false;
}
return true;
}
bool check3(int x) //判断是不是质数
{
if (x < 2) return false;
for (int i = 2; i <= x / i; i ++ )
{
if (x % i == 0) return false;
}
return true;
}
int main()
{
scanf("%d%d", &a, &b);
if (a % 2 == 0) a ++ ; //预处理 由于本题回文数从5开始判断(不用特判a=2的情况) 所有偶数(除了2)都不是质数
a = min(9999999, a); //额外优化 最大的回文质数是9989899 不加这行也能ac
for (int i = a; i <= b; i = i + 2) //这里是i+2 非常巧妙 直接规避了以后++到偶数的情况
{
//从前到后 检验当前数是不是回文质数
if (!check1(i)) continue;
if (!check2(i)) continue;
if (!check3(i)) continue;
//顺利通过以上三个检验条件后 就是回文质数 输出
printf("%d\n", i);
}
return 0;
}
优化
自己写的代码只能通过9/10组数据 最后一组会TLE
只需要加上优化就能AC
优化:b = min(9999999, b);
因为最大的回文质数是9989899 但这很取巧
可以记一下的使用方式string s = to_string(i); //将整型转化为字符串形式
#include <bits/stdc++.h>
using namespace std;
int a, b;
bool is_prime(int x)
{
if (x < 2) return false;
for (int i = 2; i <= x / i; i ++ )
{
if (x % i == 0) return false;
}
return true;
}
int main()
{
scanf("%d%d", &a, &b);
for(int i = a; i <= b; i ++ )
{
if (i % 2 == 0) continue;
string s = to_string(i); //将整型转化为字符串形式
for (int j = 0; j < s.size(); j ++ )
{
if (s[j] != s[s.size() - 1 - j]) break;
if (s.size() % 2 == 0)
{
if (j == s.size() / 2 - 1)
{
if (is_prime(i))
{
printf("%d\n", i);
break;
}
break;
}
}
else
{
if (j == s.size() - 1 - j)
{
if (is_prime(i))
{
printf("%d\n", i);
break;
}
break;
}
}
}
}
return 0;
}