当前位置: 首页 >> 程序设计 >> 数据结构和算法 >> "S/P先生数学谜题"算法分析及源代码
 

"S/P先生数学谜题"算法分析及源代码

作者:      来源:zz     发表时间:2006-11-08     浏览次数:      字号:    

S、P先生数学谜题:
 
设有两个自然数X、Y,2<=X<=Y<=99,S先生知道这两个数的和S,P先生知道这两个数的积 P ,他们二人进行了如下对话:
S:我确信你不知道这两个数是什么,但我也不知道。
 
P: 一听你说这句话,我就知道这两个数是什么了。
 
S: 我也是,现在我也知道了。
 
现在你能通过他们的会话推断出这两个数是什么吗?(当然,S和P先生都是非常聪明的)
 
S、P先生数学谜题的思路:
 
首先已声明2<=x<=y<=99,且x,y都是自然数。
 
1、S先生说:“我不知道这两个数。”,这就可知S决不是:
   4(=2+2);5(=2+3);197(=98+99);198(=99+99)。
   把这些S的不可能的值及其对应的X,Y的组合存储到数组US[]中。
 
2、S先生说:“我确定你也不知道这两个数。”,这就可知P决不是:
(1)         两端的数之积,如4(=2*2);6(=2*3);8=(2*4);10(=2*5);。。。
9801(=99*99);9702(=98*99);9604(=98*98)。
 (2) 素数或者素数之积,如5,7,11,。。。25(=5*5),35(=5*7)。
   把这些P的不可能的值及其对应的X,Y的组合存储到数组UP[]中。
UP共有2981个元素,它们是:
UP={4 5 6 7。。。4316 4327 4331 。。。9781 9787 9791 9801}
   那么可以根据这些X,Y的组合求出对应的S(=X+Y)来,把它们加入到数组US[]中。这样一来,S的数又可以排除一些了。
   比如6(=2+4);7(=2+5);...10(=5+5);...。
   如此一来,就可以初步总结出S可能的数的集合:
   S={ 11 ,17 ,23 ,27 ,29 ,35 ,37 ,41 ,47 ,53 }。
   同理,根据UP[]也初步总结出P可能的数的集合。但此时P的数目还是很大的,总计有6817个。
   P={ 12 16 18。。。4317 4318 4319。。。9798 ,9799 ,9800 }
 
   注意:因为S和P先生都是非常聪明的,所以他们都能够推出上面的S和P。
 
3、P先生说:“一听你的话,我就知道了。”,这是可以判断P的可能数值的。
   我们这样来分析,将P分解后,将得到多个X,Y的组合,分别求出每个组合对应的S(=X+Y)。
     例如,P=X*Y得到的X,Y 组合有32(=2*16=4*8),对应的S有18=2+16;12=4+8,因为18和12都不属于S,因此这两个X,Y的组合都是错的;
又如72(=2*36=3*24=4*18=6*12=8*9)也不行,因为3+24=27和8+9=17均属于S,即对于同一个P,有两个不同的X,Y组合属于S。这样P先生根本无法确定唯一的X,Y组合。
那要在什么条件下P先生才能确定唯一的X,Y组合呢?答案就是:当从P中分解出来的多个X,Y组合中有且仅有一个属于S。按照这个方法,
我们可以找出满足条件的P的集合(共86个):
P={ 18 24 28 50 52 54 76 92 96 100 110 112 114 124 130 138 140 148 152 154 160 162 168 170 172 174 176 182 186 190 198 204 208 216 232 234 238 240 246 250 252 270 276 280 282 288 294 304 306 310 336 340 348 360 364 370 378 390 400 408 414 418 430 442 480 492 496 510 520 522 532 540 550 552 570 592 612 630 646 660 672 682 690 696 700 702   }
 
P先生就可以把他所知道的P进行分解,然后把分解出来的多个X,Y组合一个个去构成对应的S,这样就可以找到唯一的X,Y组合。
但是我们毕竟不是P先生,我们并不知道真实的P值,我们知道的只是可能的P的集合,我们还必须根据第三句话,即S先生所说的“我也是,现在我也知道了。”来进一步缩小S的可能值的范围。
注意:因为S先生非常聪明,所以他能够根据P先生的话推出上面的P。
 
4、S先生说:“我也是,现在我也知道了。”,这样一来,就完全可以判断x,y的数值了。其原理和第3步相同。
     即将S分解后,将得到多个X,Y的组合,分别求出每个组合对应的P(=X*Y)。当从S中分解出来的多个X,Y组合中有且仅有一个属于P时,就能确定唯一的X,Y组合了。
   这样,S先生根据自己猜出的集合P,再把自己的S分解成x,y组合,把不同的X,Y组合放到P中去验证,求出唯一的解。
     但是我们毕竟不是S先生,我们并不知道真实的S值,我们知道的只是可能的S的集合。但幸运的是,此时的S集合中只有一个数了,那就是
S={ 17 }。于是我们也就可以和S先生一样猜出正确的答案了。
     注:如果,S的值不止一个的话,我们就可以连立方程X+Y=S和X*Y=P。然后把P和S集合中所有的值一个个代进去算,那么就可以解出满足条件的X,Y组合了,当然此时组合的个数就不一定是唯一的了。
 
编程算法分析:
     根据上面的思路,我们可以得出编程的算法:(为提高算法的通用性,我们设M=2,N=99)
1.   遍历S(M+M〈=S〈=N+N〉,分析构成S的X,Y组合的个数,把组合个数唯一的S(即不可能的S)记录到US数组中。
注意:其实此时的US是很容易推出来的,因为它只有4个数:4(=2+2);5(=2+3);197(=98+99);198(=99+99)。
2.   遍历P(M*M〈=P〈=N*N〉,分析构成P的X,Y组合的个数,把组合个数唯一的P(即不可能的P)记录到UP数组中,并同时记录构成P的组合X,Y。
注意1:如果P为素数,则它不能分解为满足条件的X,Y,因此素数也要记录到UP数组中。
注意2:为减少计算量,可累积构成P的X,Y组合的个数,一旦组合的个数超过1,立即退出循环,分析下一个P。
3.   遍历UP,把构成P的组合X,Y之和S记录下来,并加入到由第一步得到的US中。
注意:这次得到的S包含了4(=2+2);5(=2+3);197(=98+99);198(=99+99)。
所以第一步其实是不必要的。
4.   遍历S(M+M〈=S〈=N+N〉,根据得到的US,利用排除法,得到可能的S的值的集合,记录到数组S[ ]中。
5.   同理,遍历P(M*M〈=P〈=N*N〉,根据得到的UP,利用排除法,得到可能的P的值的集合,记录到数组P[ ]中。
6.   根据P先生的话,遍历数组P[ ],分析构成P的组合X,Y,判断X+Y是否属于S[],是则返回1,否则返回0。累积返回值,若最后的返回值为1,则表示从该P中分解出来的多个X,Y组合中有且仅有一个属于S。那这个P就是最新的P的可能值,把它记录到数组MUL[]中,并同时记录构成P的组合X,Y。
遍历完数组P[ ]后,把数组MUL[]复制到P[ ],得到新的数组P[ ]。
7.同理,根据S先生的话,遍历数组S[ ],分析构成S的组合X,Y,判断X*Y是否属于P[],是则返回1,否则返回0。累积返回值,若最后的返回值为1,则表示从该S中分解出来的多个X,Y组合中有且仅有一个属于P。那这个S就是最新的S的可能值,把它记录到数组ADD []中,并同时记录构成S的组合X,Y。
遍历完数组S[ ]后,把数组ADD[]复制到S[ ],得到新的数组S[ ]。
8.利用穷举法,连立方程X+Y=S和X*Y=P。然后把P和S集合中所有的值一个个代进去算,分别记录满足条件的X,Y组合到X[i]和Y[i]中,并返回其长度。
9.   印出所有满足条件的X,Y组合到X[i]和Y[i];若没有满足条件的X,Y组合,则打印“没有”。
 
注意:这种算法的特点是能够同时记录P,S以及对应的组合X,Y的值,因此需要建立一个结构
typedef struct{ //存放两个数之和或积的集合
     int x;
     int y;
     int data;
} SP1;
     这样一来,占用的空间就大,但它在函数(int put_us(SP1 *us, SP1 up[], int len_us, int len_up); //根据p的不可能的值的集合 进一步确定 s的不可能的值的集合) 和 函数(int possible_xy(SP1 p[], SP1 s[], int len_s, int len_p, int *px, int *py); //根据P和S的可能的值的集合,判断出X,Y的可能的值的集合) 中可以直接把对应的X,Y代入,而不必遍历X和Y,节省了时间。
     但是这种算法它是先求出US和UP,再遍历S,根据US得出新的S;遍历P,根据UP得出新的P。这样的计算量也是比较大的。
     为了克服这些缺点,我们可以把算法加以改进。
 
改进之算法:
1.   遍历S(M+M〈=S〈=N+N〉,分析构成S的X,Y组合的个数,并把组合的个数作为数组S[ s]的元素的值,其下标s即当前被分析的s(=x+y)。用s(=x+y)作为下标的好处很大,因为今后遍历S时,其下标就是X,Y之和。
注意1:在这里数组S[ ]的元素的值不是s(=x+y)本身,而是构成S的X,Y组合的个数。如当S=4时,构成S的X,Y组合的个数只有2+2=4,则s[4 ]=1;同理S[5]=1;S[6]=2,因为2+4=6,3+3=6;S[8]=3。
那么,值为1的元素,表示构成其下标S的X,Y组合的个数是唯一的,那么这个S是不可能的。即,若S[s(=x+y)]=1,则s(=x+y)属于US。
例如,因为S[5]=1,所以5是不可能的S值。
2.   同理,遍历P(M*M〈=P〈=N*N〉,分析构成P的X,Y组合的个数,并把组合的个数作为数组P[ p(=x*y)]的元素的值,其下标p即当前被分析的p(=x*y)。
例如,P[4]=1;P[5]=0;P[6]=1;P[12]=2,P[24]=3。
同理,若P[p(=x*y)>=1或P[p(=x*y)]=0,则该p(=x*y)]属于UP。
注意:为减少计算量,可判断P[ p(=x*y)]的元素的值,
一旦P[p(=x*y)] 〉1,立即退出循环,分析下一个P。
3.   根据P的不可能的值的集合进一步确定 S的不可能的值的集合。即,若P[p(=x*y)>=1或P [p(=x*y)]=0,则可以推出S[s(=x+y)]=1。
注意:因为此时确定的S的不可能的值的集合包含了第一步中的US,所以第一步其实是不必要的。
4.   根据S的不可能的值的集合进一步确定 S的可能的值的集合。
遍历S[ ],若S[s(=x+y)] !=1,则该s(=x+y)是可能的,把它归入数组add[ ]。然后把add[ ]复制到S[ ],得到新的数组S[ ]。
注意:此时新的数组S[ ]的元素值不再表示组合的个数,而是s(=x+y)本身,它的下标是从0开始的,其长度表示S的可能的值的个数。
5.同理,根据P的不可能的值的集合进一步确定 P的可能的值的集合。
遍历P[ ],若P[p(=x*y)] !=1并且P[p(=x*y)] !=0,则该p(=x*y)是可能的,把它归入数组mul[ ]。然后把mul[ ]复制到P[ ],得到新的数组P[ ]。
注意:此时新的数组P[ ]的元素值不再表示组合的个数,而是p(=x*y)本身,它的下标是从0开始的,其长度表示P的可能的值的个数。
6.7.8.9步骤同算法(一) 。
 
注意:在算法(二)中,P[ ]和S[ ]的元素值开始(1-3步骤)表示的是构成P或S的X,Y组合的个数,而到了第4步以后,P[ ]和S[ ]的元素值表示的是s(=x+y)和p(=x*y)本身,其长度表示P或S的可能的值的个数。
 算法(二)不用同时记录P,S以及对应的组合X,Y的值,因此不需要建立结构,而只要建立一个整型数组就可以了。

 

附录1:
根据算法(一)写出的程序代码:
#include <stdio.h>
#include <stdlib.h>
#define M 2
#define N 200

typedef struct{  //存放两个数之和或积的集合
 int x;
 int y;
 int data;
} SP1;
 
int possible_up(SP1 *up); // 寻找p的不可能的值的集合
//根据p的不可能的值的集合 进一步确定 s的不可能的值的集合
int put_us(SP1 *us, SP1 up[], int len_us, int len_up);
int out_s(SP1 us[], SP1 *s, int len);//根据s的不可能的值的集合,推出s的可能的值的集合
int out_p(SP1 up[], SP1 *p, int len); //根据p的不可能的值的集合,推出p的可能的值的集合
int judge1(SP1 *p, SP1 s[], int len_p, int len_s);//根据P的话判断出P的可能的值的集合
int judge_s(int x, int y,  SP1 s[], int len);//判断P分解的两个数之和是否属于集合S
int judge2(SP1 *s, SP1 p[], int len_s, int len_p);//根据S的话判断出S的可能的值的集合
int judge_p(int x, int y, SP1 p[], int len); //判断S分解的两个数之积是否属于集合P
//根据P和S的可能的值的集合,判断出X,Y的可能的值的集合
int possible_xy(SP1 p[], SP1 s[], int len_s, int len_p, int *px, int *py);
void print(int x[], int y[], int len);//输出最后的结果
void print1(FILE *fp, SP1 a[], int len); //输出us或up到文件
void print2(FILE *fp, SP1 a[], int len); //输出us.x,us.y或up.x,up.y到文件                                   
int Is_sushu(int n);//判断 n 是否为素数
void Paixu(SP1 *a, int len);//按从小到大排列

int main( void )
{
  FILE *fp;
  char filename[20]; //存放文件名
    int len_us=0, len_up = 0, len_s = 0, len_p = 0, len_xy = 0;
    SP1 us[N*N]={0}, up[N*N]={0}, *pus, *pup;
  SP1 s[N+N]={0}, p[N*N]={0}, *ps, *pp;
  int x[N] = {0}, y[N] = {0}, *px, *py;
 
  printf("\nEneter a filename: ");
    gets(filename);
    //为指针分配空间
    if ( (fp=fopen(filename,"w")) == NULL)
    {
        fprintf(stderr,"\nError opening file %s .\n",filename);
      exit(1);
    } 
  pus = (SP1 *)malloc(sizeof(SP1));
  if(!pus)
  {
   printf("error");
   return 1;   
  }
  pup = (SP1 *)malloc(sizeof(SP1));
  if(!pup)
  {
   printf("error");
   return 1;   
  }
  ps = (SP1 *)malloc(sizeof(SP1));
  if(!ps)
  {
   printf("error");
   return 1;   
  }
  pp = (SP1 *)malloc(sizeof(SP1));
  if(!pp)
  {
   printf("error");
   return 1;   
  }
  pus = us;   //将指针指向数组
  pup = up;
  ps = s;
  pp = p;
  px = x;
  py = y;
 
  len_up = possible_up(pup); // 寻找p的不可能的值的集合
   
  len_us = put_us(pus, up, len_us, len_up); // 进一步寻找s的不可能的值的集合     
    len_s = out_s(us, ps, len_us);  //推出s的可能的值的集合
    len_p = out_p(up, pp, len_up);  //推出的p可能的值的集合
  
  len_p = judge1(pp, s, len_p, len_s); //根据P的话判断出P的可能的值的集合

  len_s = judge2(ps, p, len_s, len_p); //根据S的话判断出S的可能的值的集合
   //根据P和S的可能的值的集合,判断出X,Y的可能的值的集合
  len_xy = possible_xy(p, s, len_s, len_p, px, py);
  print(x, y, len_xy); //输出最后的结果
 
    fclose(fp);
    system("pause");
    return 0;
}

int possible_up(SP1 *up)// 寻找p的不可能的值的集合,并返回其长度
{

 int k=0, p, x, y, flag;
 
 for(p=M*M; p<=N*N; p++)//穷举p所有的可能值,把不可能值存入数组up[]
 {
  flag = 0;
  if(Is_sushu(p)) //若p为素数,直接存入数组up[] 
  {  up[k].x = 1;
   up[k].y = p;
   up[k].data = p; 
   k++;
   continue;
  }
  for(x=M; x<=N; x++) // M<=X<=Y<=N
  {
   y = p / x;
       if(y>=x && x*y==p && y>=M && y<=N)
   { 
    flag++;
    if(flag == 1)//若只有一组x,y的值满足x+y == s,则将其存入数组 up[k]
    {
     up[k].x = x;
     up[k].y = y;
     up[k].data = p; 
     k++;
    }
    else if(flag > 1) //若有多于一组x,y的值满足x+y == s,则将其踢出数组up[k],并结束对s的分析
    {
     k--;       //将元素 us[k]删除
     break;
    } 
   }
  }
 } 
 Paixu(up, k); //将得到的 up[]排序   
 return k;
}                                                      
//根据p的不可能的值的集合 进一步确定 s的不可能的值的集合,并返回新得到的us的长度
int put_us(SP1 *us, SP1 up[], int len_us, int len_up)
{
 int i;
 
 for(i=0; i<len_up; i++)//遍历up[],如果组成它的两个数的和在(M+M ,N+N)之间,则将其和归入us[]
 {
  if(up[i].x + up[i].y >= M+M && up[i].x + up[i].y <= N+N)
  { 
   us[len_us].x = up[i].x;
   us[len_us].y = up[i].y;
   us[len_us].data = up[i].x + up[i].y;
   len_us++;
  }
 }
 Paixu(us, len_us); //将新得到的 us[]排序
 return len_us; //返回新得到的us的长度
}
//根据s的不可能的值的集合,推出s的可能的值的集合
int out_s(SP1 us[], SP1 *s, int len)
{
 int i, j=0, flag, k=0;
 
 for(i=M+M; i<=N+N; i++)   //遍历(M+M ,N+N)
 {
  flag = 0;  //因为us是有序排列的,所以只需判断比us[j].data大的数即可,而且j也随之增加,以减少运算量
  for( ; j<len && i>=us[j].data; j++)//j不用每次都从0开始,当j>0时,只需保证i>=us[j].data即可
   if(i == us[j].data) //如果i属于us,则跳出循环,并检验下一个i 
   {
    flag = 1;
    break;
   }
  if(i<us[j].data)//如果i<us[j].data,则j自减一次,以保证i>=us[j].data
   j--;  
  if(!flag) //如果i 不属于us,则将其归入s数组
   s[k++].data = i;
 } 
 return k;
}
//根据p不可能的值的集合,推出p的可能的值的集合
int out_p(SP1 up[], SP1 *p, int len)
{
 int i, j=0, flag, k=0;
 
 for(i=M*M; i<=N*N; i++)  //遍历(M*M ,N*N)
 {
  flag = 0;//因为up是有序排列的,所以只需判断比up[j].data大的数即可,而且j也随之增加,以减少运算量
  for( ; j<len && i>=up[j].data; j++)//j不用每次都从0开始,当j>0时,只需保证i>=up[j].data即可
   if(i == up[j].data) //如果i属于up,则跳出循环 ,并检验下一个i
   {
    flag = 1;
    break;
   }
  if(i<up[j].data) //如果i<up[j].data,则j自减一次,以保证i>=up[j].data
   j--;
  if(!flag) //如果i 不属于up,则将其归入p数组
   p[k++].data = i;
 } 
 return k;
}
//根据P的话判断出P的可能的值的集合
int judge1(SP1 *p, SP1 s[], int len_p, int len_s)
{
 int i, x, y, flag;
 int mx=0, my=0, k=0;
 SP1 mul[N*N]={0};
 
 for(i=0; i<= len_p; i++)//遍历p[i]
 { 
  flag = 0;
  for(x=M; x<=N; x++)
  { 
   y = p[i].data / x;
       if(y>=x && x*y==p[i].data && y>=M && y<=N)
   {
    if(flag == 0) //把第一个满足条件x*y==p[i]的x,y组合记录下来
    {
     mx = x;
     my = y;
    }
    flag += judge_s(x, y, s, len_s);//把满足条件x*y==p[i]的x,y组合放入s中进行判断
    if(flag > 1)                   //看x+y == s[i].data是否成立
     break;
   }
  }
  if(flag == 1) //如果只有唯一的一对x,y组合满足x+y == s[i].data,则将其记录在mul[]中
  {
   mul[k].x = mx;
   mul[k].y = my;
   mul[k].data = mx*my;
   k++;
  }  
 }
 for(i=0; i<k; i++)//把 mul[] 复制 到 p[]中
 {
  p[i].x = mul[i].x;
  p[i].y = mul[i].y;
  p[i].data = mul[i].data;
 }
 return k;
}

int judge_s(int x, int y, SP1 s[], int len)//判断P分解的两个数之和是否属于集合S
{
 int i;
 
 for(i=0; i<len; i++)
  if(x+y == s[i].data)
   return 1;
 return 0;
}
//根据S的话判断出S的可能的值的集合
int judge2(SP1 *s, SP1 p[], int len_s, int len_p)
{
 int i, x, y, flag;
 int mx=0, my=0, k=0;
 SP1 add[N+N]={0};
 
 for(i=0; i<= len_s; i++)//遍历s[i]
 { 
  flag = 0;
  for(x=M; x<=N; x++)
  { 
   y = s[i].data - x;
       if(y>=M && y<=N && y>=x)
   {
    if(flag == 0) //把第一个满足条件x+y==s[i]的x,y组合记录下来
    {
     mx = x;
     my = y;
    }
    flag += judge_p(x, y, p, len_p);//把满足条件x+y==s[i]的x,y组合放入p中进行判断
    if(flag > 1)                    //看x*y==p[i].data是否成立
     break;
   }
  }
  if(flag == 1) //如果只有唯一的一对x,y组合满足x*y==p[i].data,则将其记录在add[]中
  {
   add[k].x = mx;
   add[k].y = my;
   add[k].data = mx+my;
   k++;
  } 
 }
 for(i=0; i<k; i++)
 {
  s[i].x = add[i].x;
  s[i].y = add[i].y;
  s[i].data = add[i].data;
 }
 return k;
}

int judge_p(int x, int y, SP1 p[], int len)//判断S分解的两个数之积是否属于集合P
{
 int i;
 
 for(i=0; i<len; i++)
  if(x*y == p[i].data)
   return 1;
 return 0;
}

void print1(FILE *fp, SP1 a[], int len)
{
 int i;
 for(i=0; i<len; i++)
 {
  fprintf(fp, "%d  ", a[i].data);
      fprintf(stdout, "%d  ", a[i].data);
 } 
}

void print2(FILE *fp, SP1 a[], int len)
{
  int i;
  for(i=0; i<len; i++)
  {
   fprintf(fp, "%d,%d \t", a[i].x, a[i].y);
       fprintf(stdout, "%d,%d \t", a[i].x, a[i].y);
  } 
}

int Is_sushu(int n)//判断 n 是否为素数
{
 int i, flag=1;
 
 for(i=2; i<=n/2; i++)
 {
  if(n%i == 0)
  {
   flag = 0;
   break;
  } 
 }
   return flag;
}

//根据P和S的可能的值的集合,判断出X,Y的可能的值的集合
int possible_xy(SP1 p[], SP1 s[], int len_s, int len_p, int *px, int *py)
{
 int i, j, k=0;
 
 for(i=0; i<len_s; i++)
  for(j=0; j<len_p; j++)
   if(p[j].x + p[j].y == s[i].data)
   {
    px[k] = p[j].x ;
    py[k] = p[j].y ;
    k++;
   }       
 return k;
}

void print(int x[], int y[], int len)
{
 int i;
 
 if(len == 0)
  printf("No answer !!\n");
 else
  for(i=0; i<len; i++)
   printf("number %d : x=%d, y=%d\n", i+1, x[i], y[i]);
}

void Paixu(SP1 *a, int len)//按从小到大排列
{
 int i, j, min, temp;
 
 for(i=0; i<len-1; i++)
 {
  min = i;
  for(j=i; j<len; j++)
   if(a[j].data < a[min].data)
    min = j;
  if(i != min)
  {
   temp = a[i].data;
   a[i].data = a[min].data;
   a[min].data = temp;
  }
 }
}

根据算法(二)写出的程序代码:
/* S、P先生数学谜题*/

#include <stdio.h>
#include <stdlib.h>
#define M 2
#define N 99

//初步列出所有可能的p的值(M*M〈= p〈= N*N),并把每一个p的xy组合的个数作为p[i]的值
void possible1_p(int *p);
//根据p的不可能的值的集合 进一步确定 s的不可能的值的集合 ,即设该元素值为s[x+y] = 1;
void possible_us(int *s, int p[]);
//根据s的不可能的值的集合,推出s的可能的值的集合
int possible2_s(int *s);
//根据p的不可能的值的集合,推出p的可能的值的集合
int possible2_p(int *p);
//根据P的话判断出p[]的可能的值的集合
int possible3_p(int *p, int s[], int len_p, int len_s);
//根据S的话判断出s[]的可能的值的集合
int possible3_s(int *s, int p[], int len_s, int len_p);
//根据P和S的可能的值的集合,判断出X,Y的可能的值的集合
int possible_xy(int p[], int s[], int len_s, int len_p, int *px, int *py);
//输出最后的结果
void print(int x[], int y[], int len);

int main( void )
{
    int len_s=0, len_p=0, len_xy;             
    int s[N+N+1]={0}, p[N*N+1]={0}, *ps, *pp;
  int x[N]={0}, y[N]={0}, *px, *py;
 
  ps = s;
  pp = p;
  px = x;
  py = y;
 
  possible1_p(pp);//初步列出所有可能的p的值
  possible_us(ps, p);//根据p的不可能的值的集合 进一步确定 s的不可能的值的集合
  len_s = possible2_s(ps);//根据s的不可能的值的集合,推出s的可能的值的集合
  len_p = possible2_p(pp);//根据p的不可能的值的集合,推出p的可能的值的集合 
  len_p = possible3_p(pp, s, len_p, len_s);//根据P的话判断出p[]的可能的值的集合
  len_s = possible3_s(ps, p, len_s, len_p);//根据S的话判断出s[]的可能的值的集合
  //根据P和S的可能的值的集合,判断出X,Y的可能的值的集合
  len_xy = possible_xy(p, s, len_s, len_p, px, py); 
  print(x, y, len_xy);//输出最后的结果
 
    system("pause");
    return 0;
}

// 寻找p的可能的值的集合,并把构成其值的可能组合的个数作为数组p[]的元素的值
void possible1_p(int *p)
{
 int i, x, y;
 
 for(i=M*M; i<=N*N; i++) //穷举p所有的可能值,把构成其值的可能组合的个数存入数组p[]
  for(x=M; x<=N; x++) // M<=X<=Y<=N ,X*Y=P
  {
   y = i/x;
       if(y>=M && y<=N && y>=x && x*y==i)
    p[i]++;;
   if(p[i] > 1) //为节省时间,当p[i] > 1时即可跳出循环,分析下一个数
    break;
  }
}                                                      
//根据p的不可能的值的集合进一步确定 s的不可能的值的集合
void possible_us(int *s, int p[])
{
 int i, x, y;
 
 for(i=M*M; i<=N*N; i++) //穷举s所有的可能值,把构成其值的可能组合的个数存入数组s[]
   {
     if(p[i] == 1) //若构成p[i]的组合是唯一的,则该p不可能,即构成p[i]的两个数被排除
     {
       for(x=M; x<=N; x++) // M<=X<=Y<=N ,X*Y=P
       {
       y = i/x;
        if(y>=M && y<=N && y>=x && x*y==i)
     s[x+y] = 1;//被排除的两个数所构成的s[x+y]元素也被排除
       }
     }
  }
}
//根据s的不可能的值的集合进一步确定 s的可能的值的集合
int possible2_s(int *s)
{
 int i, k=0, add[N+N]={0};
 
 for(i=M+M; i<=N+N; i++) //穷举s所有的可能值,数组元素表示构成其值的xy可能组合的个数
   {
    if(s[i] != 1) //若构成s[i]的组合不是唯一的,则该s是可能的,把它归入add[k]
       add[k++] = i;
   }
   for(i=0; i<k; i++)//把add[k]复制到s[i],得到新的数组s[i]
    s[i] = add[i]; //注意此时s[i]中元素的值不再表示构成其值的xy可能组合的个数
   return k;         //而是表示一个可能的和
}
//根据p不可能的值的集合,推出p的可能的值的集合
int possible2_p(int *p)
{
 int i, k=0, mul[N*N]={0};
 
 for(i=M*M; i<=N*N; i++) //穷举p所有的可能值,数组元素表示构成其值的xy可能组合的个数
   {
    if(p[i] != 1 && p[i] != 0)  //若构成p[i]的组合不是唯一的,则该p是可能的,把它归入 mul[k]
       mul[k++] = i;
   }
 for(i=0; i<k; i++) //把mul[k]复制到p[i],得到新的数组p[i]
    p[i] = mul[i]; //注意此时p[i]中元素的值不再表示构成其值的xy可能组合的个数
   return k;        //而是表示一个可能的积
}
//根据P的话判断出p[]的可能的值的集合
int possible3_p(int *p, int s[], int len_p, int len_s)
{
 int i, j, x, y;
 int k=0, sum=0, mul[N*N]={0};
 
 for(i=0; i<len_p; i++)
   {                  
    sum=0;
    for(x=M; x<=N; x++) // M<=X<=Y<=N ,X*Y=P
    {
   y = p[i] / x;
   if(y>=M && y<=N && y>=x && x*y==p[i])
    for(j=0; j<len_s; j++)
     if(x+y == s[j])
      sum++;  //累积满足x*y==p[i]的xy的组合在s中出现(即同时满足x+y == s[j])的次数
  }
  if(sum == 1) //若只出现一次,则该xy组合为正确答案之一 ,所以P先生率先知道了答案
   mul[k++] = p[i]; //把对应的x*y==p[i]存入数组mul[k] 
 } 
 for(i=0; i<k; i++) //把mul[k]复制到p[i],得到新的数组p[i]
    p[i] = mul[i]; //注意,S先生很聪明,当P先生说知道答案后,S先生也推出了数组p[i]  
   return k;  
}
//根据S的话判断出s[]的可能的值的集合
int possible3_s(int *s, int p[], int len_s, int len_p)
{
 int i, j, x, y;
 int k=0, sum=0, add[N+N]={0};
 
 for(i=0; i<len_s; i++)
 {
  sum=0;
    for(x=M; x<=N; x++) // M<=X<=Y<=N ,X*Y=P
    {
   y = s[i] - x;
   if(y>=M && y<=N && y>=x)
    for(j=0; j<len_p; j++)
     if(x*y == p[j])
      sum++;  //累积满足x+y==s[i]的xy的组合在p中出现(即同时满足x*y == p[j])的次数
  }
  if(sum == 1)    //若只出现一次,则该xy组合为正确答案之一 ,所以S先生也知道了答案
    add[k++] = s[i];//把对应的x+y==s[i]存入数组add[k] 
   }
   for(i=0; i<k; i++) //把add[k]复制到s[i],得到新的数组s[i]
    s[i] = add[i];
 return k;
}
//根据P和S的可能的值的集合,判断出X,Y的可能的值的集合
int possible_xy(int p[], int s[], int len_s, int len_p, int *px, int *py)
{
 int x, y, i, j, k=0;
 
 for(i=0; i<len_s; i++) //遍历s[]
 {  
  for(x=M; x<=s[i]; x++)
  {
   y = s[i] - x;
   if(y>=M && y<=N && y>=x)
    for(j=0; j<len_p; j++) //遍历p[]
     if(x*y == p[j])
     {
      px[k] = x;   //解不定方程x+y =  s
      py[k] = y;            // x*y =  p
      k++;
     }       
  }
 }
 return k;
}
//输出最后的结果
void print(int x[], int y[], int len)
{
 int i;
 
 if(len == 0)
  printf("No answer !!\n");
 else
  for(i=0; i<len; i++)
   printf("number %d : x=%d, y=%d\n", i+1, x[i], y[i]);

}

 

责任编辑 webmaster

 
 
 
 
 
评论更多>>
 
程序太长了一点!!! #include<iostream.h> bool zhishu(int x);//x是质数 bool multiP(int x);//x不能分拆成两个质数的和(S先生说:我知道你不知道这两个数是什么) bool multiS(int x);//对x的各个因式分解a*b中,只有一种分解法的两因数和a+b满足multiP(a+b)(P先生说:现在我知道了) bool multi(int x);//对x的各个分拆a+b中,只有一种分拆法的积a*b满足multi(a*b)(S先生说:现在我也知道了) main() { int flag=0; for(int x=2;x<99;x++) for(int y=x;y<99;y++) if((x+y>5)&&multiP(x+y)&&multiS(x*y)&&multi(x+y)) { cout<<'('<<x<<','<<y<<')'<<endl; flag++; } if(!flag) cout<<"There is no answer!"<<endl; return 0; } bool multi(int x) { int m,n; int num=0; for(m=2;m<x-1;m++) { n=x-m; if(m<=n&&n<=99&&multiS(m*n)) num++; } return (num==1)?true:false; } bool multiS(int x) { int m,n,num=0; for(m=2;m<x;m++) if(x%m==0) { n=x/m; if(m<=n&&n<=99&&multiP(m+n)) num++; } return (num==1)?true:false; } bool multiP(int x) { int m,n; for(m=2;m<x-1;m++) { n=x-m; if(m<=n&&n<=99&&zhishu(m)&&zhishu(n)) return false; } return true; } bool zhishu(int x) { for(int i=2;i<x;i++) if(x%i==0&&x>2) return false; return true; }
 
 
发表
 
姓名: QQ:
性别: MSN:
E-mail: 主页:
评分: 1 2 3 4 5
评论内容:
验证码:
  
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •