zoukankan      html  css  js  c++  java
  • 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);
    }
    
  • 相关阅读:
    uva 408 Uniform Generator
    Java实现 蓝桥杯VIP 算法提高 栅格打印问题
    Java实现 蓝桥杯VIP 算法提高 栅格打印问题
    Java实现 蓝桥杯VIP 算法提高 栅格打印问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 不同单词个数统计
    Java实现 蓝桥杯VIP 算法提高 不同单词个数统计
  • 原文地址:https://www.cnblogs.com/yinzm/p/7430038.html
Copyright © 2011-2022 走看看