USACO Section 1.5 Prime Palindromes 解题报告

题目

题目描述

题目就是给定一个区间[a,b](5 <= a < b <= 100,000,000)),我们需要找到这个区间内所有既是回文串又是素数的数字。

输入样例

5 500

输出样例

5
7
11
101
131
151
181
191
313
353
373
383

解题思路

因为数据范围特别大,如果我们直接枚举所有的素数然后再判断是不是回文串的话肯定会超时。在题目的下面有hints,其中就告诉我们要逆向思维,既然我们枚举素数太多了,那么我们就可以先枚举出所有可能的回文串(这个数量比较少),然后再判断回文串是不是素数。下面就直接模拟这个过程就好了,在写代码的时候,主要是枚举回文串比较费劲,要细心一点。

解题代码

/*
ID: yinzong2
PROG: pprime
LANG: C++11
*/
#define MARK
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int len, alen, blen;
char astr[12],bstr[12], str[12];

bool isPrime() {
    int x = 0;
    int temp = 1;
    // 先转化为int型整数,然后再判断是否是素数
    for (int i = len-1; i >= 0; --i) {
        x += (temp*(str[i]-'0'));
        temp *= 10;
    }
    int bound = (int)sqrt(x*1.0);
    for (int i = 2; i <= bound; ++i) {
        if (x%i == 0) {
            return false;
        }
    }
    return true;
}

void judge() {
    // 保证要我们要求的区间内
    // 如果回文串的长度位于alen与blen之间,那么就代表已经是位于区间内了
    if (len == alen && strcmp(str, astr) < 0) return ;
    if (len == blen && strcmp(str, bstr) > 0) return ;
    if (isPrime()) {
        cout << str << endl;
    }
}

// cur用来记录现在产生的是回文串的第几位
void makePalindromes(int cur) {
    if (cur == len/2) {
        if (len%2 == 0) { // 产生偶数长度的
            str[len] = '';
            judge();
        } else { // 产生奇数长度的回文串,中间的那个位置可以为任意数字
            for (int i = 0; i <= 9; ++i) {
                str[cur] = i+'0';
                str[len] = '';
                judge();
            }
        }
        return ;
    }
    if (cur == 0) { // 在规定了长度的情况下,首位不能为0
        for (int i = 1; i <= 9; ++i) {
            str[cur] = i+'0';
            str[len-1-cur] = str[cur]; // 两端对称
            makePalindromes(cur+1);
        }
    } else {
        for (int i = 0; i <= 9; ++i) {
            str[cur] = i+'0';
            str[len-1-cur] = str[cur];
            makePalindromes(cur+1);
        }
    }
}

int main() {
#ifdef MARK
    freopen("pprime.in", "r", stdin);
    freopen("pprime.out", "w", stdout);
#endif // MARK
    while (~scanf("%s%s", astr, bstr)) {
        alen = strlen(astr);
        blen = strlen(bstr);
        // 枚举所有可能长度的回文串
        for (len = alen; len <= blen; ++len) {
            makePalindromes(0);
        }
    }
    return 0;
}

官方题解

官方总共给出了四种代码,但是我觉得前两种代码是比较好理解的。具体的思路在这里,其中第二种方法相对于第一种方法有一个很妙的剪枝。

官方代码1

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>

FILE *fout;
long a, b;

int
isprime(long n)
{
    long i;

    if(n == 2)
    return 1;

    if(n%2 == 0)
    return 0;

    for(i=3; i*i <= n; i+=2)
    if(n%i == 0)
        return 0;

    return 1;
}

void
gen(int i, int isodd)
{
    char buf[30];
    char *p, *q;
    long n;

    sprintf(buf, "%d", i);

    p = buf+strlen(buf);
    q = p - isodd;

    while(q > buf)
    *p++ = *--q;
    *p = '';

    n = atol(buf);
    if(a <= n && n <= b && isprime(n))
    fprintf(fout, "%ld
", n);
}

void
genoddeven(int lo, int hi)
{
    int i;

    for(i=lo; i<=hi; i++)
        gen(i, 1);

    for(i=lo; i<=hi; i++)
        gen(i, 0);
}

void
generate(void)
{
    genoddeven(1, 9);
    genoddeven(10, 99);
    genoddeven(100, 999);
    genoddeven(1000, 9999);
}

void
main(void)
{
    FILE *fin;

    fin = fopen("pprime.in", "r");
    fout = fopen("pprime.out", "w");
    assert(fin != NULL && fout != NULL);

    fscanf(fin, "%ld %ld", &a, &b);

    generate();
    exit (0);
}

官方代码2

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>

FILE *fout;
long a, b;

int
isprime(long n)
{
    long i;

    if(n == 2)
        return 1;

    if(n%2 == 0)
        return 0;

    for(i=3; i*i <= n; i+=2)
        if(n%i == 0)
                return 0;

    return 1;
}

void
gen(int i)
{
    char buf[30];
    char *p, *q;
    long n;

    sprintf(buf, "%d", i);

    p = buf+strlen(buf);
    q = p - 1;

    while(q > buf)
            *p++ = *--q;
    *p = '';

    n = atol(buf);
    if(a <= n && n <= b && isprime(n))
        fprintf(fout, "%ld
", n);
}

void
generate(void)
{
    int i;
    for (i = 1; i <= 9; i++)
      gen(i);

    if(a <= 11 && 11 <= b)
      fprintf(fout, "11
");

    for (i = 10; i <= 9999; i++)
      gen(i);
}

void
main(void)
{
    FILE *fin;

    fin = fopen("pprime.in", "r");
    fout = fopen("pprime.out", "w");
    assert(fin != NULL && fout != NULL);

    fscanf(fin, "%ld %ld", &a, &b);

    generate();
    exit (0);
}
原文地址:https://www.cnblogs.com/yinzm/p/7430038.html