[usaco] 回数中的素数Prime Palindromes

题目给出一个下限,一个上限,要求求出之间的所有既是回数又是素数的数。

下边是原文描述:

Prime Palindromes

The number 151 is a prime palindrome because it is both a prime number and a palindrome (it is the same number when read forward as backward). Write a program that

finds all prime palindromes in the range of two supplied numbers a and b (5 <= a < b <= 100,000,000); both a and b are considered to be within the range .

PROGRAM NAME: pprime

INPUT FORMAT

Line 1:

Two integers, a and b

SAMPLE INPUT (file pprime.in)

5 500

OUTPUT FORMAT

The list of palindromic primes in numerical order, one per line.

SAMPLE OUTPUT (file pprime.out)

5
7
11
101
131
151
181
191
313
353
373
383
解题的要点在于:
首先构造回数,然后判断是不是素数。如果你从下限遍历到上限,然后判断是不是回数和素数的话,你会死的很惨。
构造回数也是有技巧的。
在这里有两种方法:
1:
- - - - - - Aw-1 Aw-2.......Ai......A0
↑ ↑                               | |
| |_______________________________↓ |
|___________________________________↓

  另一种方法:

Aw-1 Aw-2.......Ai......A0 - - - - - -
↓ ↓                                ↑ ↑
| |________________________________| |
|____________________________________|
 
如果有这样一个数:
101,的话,第一种方法是无法构造出来的。
所以采用第二种方法。

第二个关键点在于判断素数。

判断素数要从2开始,直到一个上限,但上限也是有窍门的,究竟遍历到什么地方才算节省时间呢?

注意到,如果a*b=n;

那么a<根n<b;或者相反,

因此,只需要遍历到sqrt(n)即可。

第三个要点在于构造回数的起点,如果给出的起点很高的话,从1遍历是很不合算的。

在测试用例中就分为这几种情况

low          high

非常小    非常小

非常小    非常大

非常大    非常大

这是我的解法:

/*
ID: yunleis2
PROG: pprime
LANG: C++
*/

#include<iostream>
#include<fstream>
#include<cmath>
#include<string.h>
#include<vector>
using namespace std;

int longest_size=0;
bool isprime(int n);
void  getNum1(int a,char * tmp,int *r1,int *r2);
void quicksort(int * a,int p,int r);
int main()
{


fstream fin("pprime.in",ios::in);


int low,high;


fin>>low>>high;

vector<int> v;


int t=high;


int lowsize=0;


while(t!=0)


{


  longest_size++;
  t/=10;


}
t=low;
while(t!=0)
{
  lowsize++;
  t/=10;
}
char * tmp=new char[longest_size*2];
/************************************************************************/
/* search from num that has (lowsize+1)/2  only search nums not divided by 2                                                                     */
/************************************************************************/
int from=pow((double)10,(lowsize+1)/2-1);
if(from%2==0)
  from++;
int to=pow((double)10,(longest_size+1)/2);
while(from<=to)
{
  int r1=0;


  int r2=0;


  getNum1(from,tmp,&r1,&r2);
  
/*  if(r1<=high&&r1>=low)
   cout<<r1;
  cout<<"\t";

  if(r2<=high&&r2>=low)
   cout<<r2;
  cout<<endl;


  */
  from++;
 
  /************************************************************************/
  /* next step is to find where the num is prime                          */
  /************************************************************************/
  if(r1<=high&&r1>=low&&isprime(r1))
   v.push_back(r1);
  if(r2<=high&&r2>=low&&isprime(r2))
   v.push_back(r2);
}

int * result=new int[v.size()];
for(int i=0;i<v.size();i++)
{
//  cout<<v.at(i)<<endl;
  result[i]=v.at(i);
}


delete []tmp;
quicksort(result,0,v.size()-1);
fstream fout("pprime.out",ios::out);
for(int i=0;i<v.size();i++)
{
  fout<<result[i]<<endl;
}


  // system("pause");
}
void  getNum1(int a,char * tmp,int *r1,int *r2)
{
memset(tmp,0,longest_size);
int a1=a;
for(int i=0;a1!=0;i++)
{
  tmp[i]='0'+a1%10;
  a1/=10;
}
int size=strlen(tmp);


 *r1=a*pow((double)10,size-1);


 *r2=*r1*10;
for(int i=1;i<size;i++)
{
  int tr=((int)pow((double)10,(size-1)-i))*(tmp[i]-'0');


  *r1+=tr;


  *r2+=tr;
}


 *r2+=(tmp[0]-'0')*pow(10.0,size-1);

}
bool isprime(int n)
{
int i;
for(i=2;i<=sqrt((double)n);i++)
  if(n%i==0) return false;
if(i>sqrt((double)n)) return true;

}
void swap(int * x,int * y)


{


int t=*x;
*x=*y;
*y=t;
}
int partition(int* a,int p,int r)
{
int i=p-1;
int x=a[r];
for(int j=p;j<r;j++)
{
  if(a[j]<=x)
  {
   i++;
   swap(a+j,a+i);
  }
}
swap(a+i+1,a+r);return i+1;
}
void quicksort(int * a,int p,int r)
{
if(p<r)
{

 


  int q=partition(a,p,r);
  quicksort(a,p,q-1);
  quicksort(a,q+1,r);
}
}

这是测试结果:

USER: Ma yunlei [yunleis2]
TASK: pprime
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.000 secs, 3032 KB]
   Test 2: TEST OK [0.000 secs, 3032 KB]
   Test 3: TEST OK [0.000 secs, 3032 KB]
   Test 4: TEST OK [0.000 secs, 3032 KB]
   Test 5: TEST OK [0.000 secs, 3032 KB]
   Test 6: TEST OK [0.000 secs, 3032 KB]
   Test 7: TEST OK [0.081 secs, 3032 KB]
   Test 8: TEST OK [0.054 secs, 3032 KB]
   Test 9: TEST OK [0.081 secs, 3032 KB]

All tests OK.

Your program ('pprime') produced all correct answers! This is your

submission #4 for this problem. Congratulations!

Here are the test data inputs:

------- test 1 ----
5 500
------- test 2 ----
750 14000
------- test 3 ----
123456 1123456
------- test 4 ----
97000 1299000
------- test 5 ----
9878210 9978210
------- test 6 ----
9902099 9902100
------- test 7 ----
7 10000000
------- test 8 ----
1333331 9743479
------- test 9 ----
5 100000000
Keep up the good work!

这是uaco的解法:

The main problem here is that we need some way to generate palindromes. Since there are only about 10,000 palindromes less than 100,000,000, we can just test each one to see if it is prime and in the range.

To generate a palindrome, we start with the first half and reverse it. The trick is that we can repeat the middle character or not repeat the middle character. I call a palindrome with a repeated middle character "even" (because it is of even length) and one without "odd". So from the string "123", we can generate the even palindrome "123321" or the odd palindrome "12321".

We can generate all palindromes by doing the following:

  • length 1: generate odd palindromes using 1..9
  • length 2: generate even palindromes using 1..9
  • length 3: generate odd palindromes using 10..99
  • length 4: generate even palindromes using 10..99
  • ...

The "generate" function does exactly this, using "genoddeven" to first generate the odd palindromes for a range and then the even ones.

The "gen" function generates an even or odd palindrome for a number by converting it to a string, making a palindrome, and converting the resulting string back to a number. Then it tests to see if the number is in the range and prime. If so, it is printed.

We could speed this up in a number of ways: use a faster primality test, don't generate palindromes past "b", etc. But this is already plenty fast, and doing such things makes the program more complex and might introduce bugs.

#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 = '\0';

    n = atol(buf);
    if(a <= n && n <= b && isprime(n))
	fprintf(fout, "%ld\n", 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);
}
 

master_zed writes:

 

 

The problem can be simplified slightly by noticing that any even palindrome is divisible by 11. Therefore, 11 is the ONLY even prime palindrome. This eliminates the need to deal with 2 cases:

#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 = '\0';

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

void
generate(void)
{
    int i;
    for (i = 1; i <= 9; i++)
      gen(i);
    if(a <= 11 && 11 <= b)
      fprintf(fout, "11\n");

    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);
}

Coach Rob writes:

I guess I have a slightly different coding style than the previous two solutions. This is the same idea but coded a bit more tightly (thanks to Michael Coblenz for its kernel):

 

 

#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
int primelist[100000];
int nprimes;

int isPrime(int num);
int reverse2(int i, int j);
int compare(const void *p, const void *q) { return *(int *)p-*(int *)q; }
void main (void) {
    ifstream infile("pprime.in");
    ofstream outfile("pprime.out"); 
    int i, j, begin, end, num;
    infile>>begin>>end;
    if (begin <= 11 && 11 <=end)
        primelist[nprimes++] = 11;
    for (j = 0; j <= 999; j++)
        for (i = 0; i <= 9; i++)  {
	    num = reverse2(j,i);
	    if (num >= begin && num <=end && isPrime(num)) 
  	        primelist[nprimes++] = num;
        }
    qsort(primelist, nprimes, sizeof(int), compare);
    for (i = 0; i < nprimes; i++)
	outfile << primelist[i] << "\n";
}

int
reverse2(int num, int middle) {
    int i, save=num, digit, combino = 1;
    for (i = 0; num; num /= 10) {
	digit = num % 10;
	i = 10 * i + digit;
	combino *= 10;
    }
    return i+10*combino*save+combino*middle;
}
	
int isPrime(int num) {
    int i;
    if (num <= 3) return 1;
    if (num%2 == 0 || num%3 ==0) return 0;
    for (i = 5; i*i <= num; i++)
	if (num %i ==0)
	    return 0;
    return 1;
}

And here is another tightly coded solution from Anton Titov:

 

I guess you may find intresting my solution to Prime Palindromes as I use a function to generate palindromes, that is purely arythmetical it does not use strings, sprintf, reversion or other things. It uses recursion, but my guess is that it will outpreform all other functions listed.

 

Function void genPalind(int num, int add, int mulleft, int mulright)

 

 

expects 4 parameters, first is the number (N) of digits you want for your palindromes, second should be 0 for direct calls, third should be 10^(N-1) for direct calls and last one should be 1 for direct calls.

 

 

#include <stdio.h>
#include <string.h>
 
#include <stdlib.h>
#include <math.h>
FILE *f;
int a, b;

int isPrime(int num);
void genPalind(int num, int add, int mulleft, int mulright);
void tryPalind(int num);
int main(){
  int i;
  char first;
  f=fopen("pprime.in", "r");
  fscanf(f, "%d%d", &a, &b);
  fclose(f);
  f=fopen("pprime.out", "w");
  if (a<=5)
    fprintf(f, "%i\n", 5);
  if (a<=7 && b>=7)
    fprintf(f, "%i\n", 7);
  if (a<=11 && b>=11)
    fprintf(f, "%i\n", 11);
 
  genPalind(3, 0, 100, 1);
  genPalind(5, 0, 10000, 1);
  genPalind(7, 0, 1000000, 1);
  fclose(f);
}

void tryPalind(int num){
  if (!(num&1))
    return;
  if (num<a || num>b)
    return;
  if (!(num%3) || !(num%5) || !(num%7))
    return;
  if (!isPrime(num))
    return;
  fprintf(f, "%d\n", num);
}

void genPalind(int num, int add, int mulleft, int mulright){
  int i, nmulleft, nmulright;
  if (num==2){
    for (i=0; i<10; i++)
      tryPalind(add+mulleft*i+mulright*i);
  }
  else if (num==1){
    for (i=0; i<10; i++)
      tryPalind(add+mulright*i);
  }
  else {
    nmulleft=mulleft/10;
    nmulright=mulright*10;
    num-=2;
    for (i=0; i<10; i++)
      genPalind(num, add+i*mulleft+i*mulright, nmulleft, nmulright);
  }
}

int isPrime(int num){
  int koren, i;
  koren=(int)sqrt(1.0*num);
  for (i=11; i<=koren; i+=2)
    if (!(num%i))
      return 0;
  return 1;
 
}


### 回答1: 题目描述: 给定一个整数 $N$,求出大于 $N$ 的最小的既是质数又是回文数的数。 回文数指的是正着读和倒着读都一样的数字,例如 12321 就是一个回文数。 输入格式: 输入共 1 行,包含一个整数 $N$。 输出格式: 输出共 1 行,包含一个整数,表示题目所求的数。 数据范围: $1≤N≤10^7$ 样例: 输入: 31 输出: 101 解题思路: 从 $N$ 开始遍历,判断每一个数是否既是质数又是回文数。如果找到了这样的数,直接输出即可。 判断是否为质数可以用较为简单的暴力算法,枚举 $2$ 到 $\sqrt{x}$ 之间的所有数,看是否存在约数。 判断是否为回文数可以将该数转化为字符串,然后比较正序字符串和倒序字符串是否相等即可。 注意,本题所求的数可能非常大,需要使用 long long 类型存储,并且需要使用快速幂算法来快速计算幂次。同时,因为奇数位的回文数一定不是 11 的倍数,因此可以只枚举奇数位的回文数。 ### 回答2: 题目要求找出范围在2到N(包括2和N)之间的回文质数。所谓回文质数是指既是质数又是回文数的数。质数是指除了1和自身以外没有其他因数的正整数。 首先,我们先定义两个函数:一个是用来判断一个数是否为质数的函数is_prime,另一个是用来判断一个数是否为回文数的函数is_palindrome。 is_prime函数的实现方法如下:从2到该数的平方根进行遍历,判断是否存在该数的因数,如果存在则返回False,代表不是质数,如果遍历结束都没有找到因数,则返回True,代表是质数。 is_palindrome函数的实现方法如下:将该数字转化为字符串,并判断该字符串与其翻转后的字符串是否相等,如果相等则返回True,代表是回文数,否则返回False,代表不是回文数。 接下来,我们在范围从2到N进行遍历,对每个数字都进行is_prime和is_palindrome的判断,如果都满足条件,则将该数字输出。 下面是代码实现的伪代码: ``` function is_prime(num): if num < 2: return False for i in range(2, int(num**0.5)+1): if num % i == 0: return False return True function is_palindrome(num): num_str = str(num) if num_str == num_str[::-1]: return True return False function prime_palindromes(N): for num in range(2, N+1): if is_prime(num) and is_palindrome(num): print(num) ``` 以上是本题的解题思路和伪代码实现,希望能对你有所帮助。 ### 回答3: 题目要求找出所有小于等于N的回文质数。 回文数是指正读反读都相同的数,例如121、12321都是回文数。质数是只能被1和自身整除的数,例如2、3、5、7都是质数。 首先,我们可以编写一个函数来判断一个数是否为质数。函数的输入是一个正整数n,判断n是否能被小于n的所有数整除,如果能则返回False,否则返回True。 接下来,我们可以编写一个函数来判断一个数是否为回文数。函数的输入是一个正整数n,将n转换成字符串并反转,然后与原字符串进行比较,如果相同则返回True,否则返回False。 在主函数,我们可以遍历1到N之间的所有数,对于每个数,首先判断是否为回文数,如果不是则跳过;然后判断是否为质数,如果是则输出该数。 最后,我们可以将上述步骤封装成一个循环,将N从2逐渐增加,直到N超过题目要求的上限。 以下是代码实现: def is_prime(n): for i in range(2, n): if n % i == 0: return False return True def is_palindrome(n): s = str(n) if s == s[::-1]: return True return False N = int(input()) for n in range(2, N + 1): if is_palindrome(n) and is_prime(n): print(n) 希望能够帮助你解答问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值