洛谷P1217 [USACO1.5] 回文质数 Prime Palindromes

原题地址:[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;
                        }
                    }
                }
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值