早晨突然发现前些日子,某个酒足饭饱的午后,黄哥问的一个傻问题——当时也没有听清楚——居然是一道编程题!而且是很有难度的一道,呵呵,难得黄哥能问出这种问题!!决定解决之~~~~~~~~~~~~
题目为(ACM/ICPC Regional Contest Southeastern European 2001. Problem C. Secret Numbers),原文如下:
Problem C
Secret Numbers
Problem
Two natural numbers a and b are chosen (1<a<b). Person M is told the multiple of a and b (a*b), and person S is told the sum of a and b (a+b). The discussion between M and S goes like this:M: I do not know the numbers a and b.
S: I do not know them either, but I knew you would not know them.
M: Now I know the numbers!
S: Now I know them, too!
Input
The input file contains pairs of natural numbers x and y (2<=x<y<=550), one pair per line. The input is guaranteed to be correct.
Output
For each pair x, y, find all pairs of a and b, such that x<=a<b<=y and that the given discussion is possible. Write these pairs in a single line, and finish that line with "no more pairs." if there are a and b found in the given range, or write simply "no pairs." if there are not. Separate the numbers of a pair with a comma, terminate each pair with a semi-colon, and separate different pairs with a blank after the semi-colon, as shown in the example below.
Sample Input
2 10 2 20
Output for the Sample Input
no pairs. 4,13; no more pairs.
简单解释一下:
有两个数a和b(1<a<b)。M先生知道a*b的值,S先生知道a+b的值,两人有如下的对话:
M先生: 我不知道a和b的值。
S先生: 我也不知道,而且之前我还知道你不知道。
M先生: 我现在知道a和b的值了。
S先生: 我现在也知道a和b的值了。
给定x和y(2<=x,y<=550),求所有满足对话场景的(a,b)且x<=a<b<=y。假设二位先生都足够聪明且没有撒谎!
你有想法了吗???
幸好足够聪明的博得先生,在参考了N本书后,终于搞定了这个问题!下面来分析一记:
解题的关键是MS两位先生的四句话,而且这四句话是步步递进的,也就是任何满足后一句的数对,必然满足前一句!如果可以将这四句话用四个逻辑判别函数( f1(a,b)~f4(a,b) )表示,就可以在给定的范围内求出所有的满足f4(a,b)的数对!
首先,根据题意先设有两个集合:
M(a,b)={ (x,y)| x*y=a*b 且 1<x<y }
S(a,b)={ (x,y)| x+y=a+b 且 1<x<y }
可以得到如下表格:
MS两位先生的话 | 实现函数 | 条件 | 返回值 |
M:我不知道a和b的值。 | bool f1(int a,int b) | |M(a,b)|>1 | true |
else |
false | ||
S:我也不知道,而且之前我还知道你不知道。 | bool f2(int a,int b) | 1、f1(a,b)==true 2、|S(a,b)|>1 3、任取(p,q)∈S(a,b)使得f1(p,q)==true |
true |
else |
false | ||
M:我现在知道a和b的值了。 | bool f3(int a,int b) | 1、f2(a,b)==true 2、只有一对(p,q)∈M(a,b) 满足f2(p,q)==true |
true |
else |
false | ||
S:我现在也知道a和b的值了。 | bool f4(int a,int b) | 1、f3(a,b)==true 2、只有一对(p,q)∈S(a,b) 满足f3(p,q)==true |
true |
else |
false |
具体的实现就看代码吧!
//Problem C. Secret Numbers
//by BodeSmile 05.10.03
//
// M(a,b)={ (x,y)| x*y=a*b 且 1<x<y }
// S(a,b)={ (x,y)| x+y=a+b 且 1<x<y }
#include <iostream>
#include <cmath>
using namespace std;
#define MAXN 302500
#define MAXM 550
bool prime[MAXN];
//素数表的生成
void inline get_prime()
{
int i,j,k;
prime[0]=prime[1]=true;
for(i=4;i<MAXN;i+=2)
prime[i]=true;
for(i=3;i<MAXM;i+=2)
{
if(prime[i])
continue;
k=i+i;
for(j=i*i;j<MAXN;j+=k)
prime[j]=true;
}
}
bool f1(int a,int b)
{
//|M(a,b)|>1
if( !prime[a] && ( (!prime[b])||(b==a*a)||(b==a*a*a) ) )
return false;
else
return true;
}
bool f2(int a,int b)
{
int tp;
if( f1(a,b) && (a+b)>6 )
{
//p、q均为质数,使用哥德巴赫猜想!
//(a+b为偶数,必然存在质数p和q,使:p+q==a+b)
if( (a+b)%2==0 )
return false;
else if( !prime[a+b-2] )
return false;
//p为质数,q=p*p或q=p*p*p 的情况
tp=(int)sqrt((double)(a+b));
if( tp*(tp+1)==(a+b) && (!prime[tp]) )
return false;
for(tp=2;(tp+tp*tp*tp)<=(a+b);tp++)
{
if( prime[tp] )
continue;
if( (tp+tp*tp*tp)==(a+b) )
return false;
}
//条件都满足,就返回true
return true;
}
else
return false;
}
bool f3(int a,int b)
{
int i;
double tp;
if( f2(a,b) )
{
tp=sqrt((double)(a*b));
for(i=2;i<tp;i++)
{
if( (a*b)%i!=0 )
continue;
if( a==i )
continue;
if( f2(i,(a*b)/i) )
return false; //只有一对(p,q)∈M(a,b)满足f2(p,q)==true
}
return true;
}
else
return false;
}
bool f4(int a,int b)
{
int i;
if( f3(a,b) )
{
for(i=2;i<((a+b)/2);i++)
{
if( a==i )
continue;
if( f3(i,(a+b)-i) )
return false; //只有一对(p,q)∈S(a,b)满足f3(p,q)==true
}
return true;
}
else
return false;
}
int main()
{
int tp;
int i,j;
int x,y;
get_prime();
while(cin>>x>>y)
{
tp=0;
for(i=x;i<y;i++)
{
for(j=i+1;j<=y;j++)
{
if( f4(i,j) )
{
cout<<i<<","<<j<<"; ";
tp++;
}
}
}
if(tp==0)
cout<<"no pairs.\n";
else
cout<<"no more pairs.\n";
}
return 0;
}
执行一下,输入两个数(2~550之间)比如输入2、100就可以知道在 2~100 之间,满足条件的有四组:(4,13)、(4,61)、(16,73)、( 64,73)。
至此程序应该是对的了,但由于没有找到哪个Online Judge上有这题,所以并没有递交过。那就欢迎大家的指正了!!!
参考书目:《算法艺术与信息学竞赛》