题目链接:http://poj.org/problem?id=3101
大致题意:一个恒星系统中有n(2 ≤ n ≤ 1 000)颗行星,告诉你它们的运转周期Ti(1 ≤ ti ≤ 10 000),求他们能够同线的最小周期,表示形式是:分子 分母
Time Limit: 2000MS | Memory Limit: 65536K |
样例:
Sample Input
3 6 2 3
Sample Output
3 1
思路:仔细看图就知道任意两颗行星共线,则 x/t1 - x/t2 = 0 mod (1/2),运行距离差为半个周长的整数倍即可,整理得到(t2-t1)*x/(t1*t2) = 0 mod (1/2),这样的方程可以列n*(n-1)/2个。但不需要这么多,我们都以第一颗行星作为标尺 x/t1 - x/t2 = 0 mod(1/2) ,x/t1 - x/t3 = 0 mod(1/2),两式相减得到 x/t3 - x/t2 = 0 mod(1/2)
所以只需要这n-1个公式成立即可。再将上述式子变形:x*(2/t1-2/t2)=0 mod 1,那么问题就转化为求(2/t1-2/ti) (2 ≤ i ≤ n)中分母的最小公倍数和分子的最大公约数,最小公倍数的答案的分子,最大公约数是答案的分母。想不通的找几个分数通分一下就懂了。
一开始用的是简单求lcm,提交WA了。回头看看数据范围,1 ≤ ti ≤ 10 000,考虑极限情况,如果把10000以内的质数大小都看作是10^4 那么1000个数的乘积就是10^4000次方。这样计算,这个题求LCM还需要高精度,求的最大公约数就不用了,最大公约数明显小与10000。1.可以用JAVA大数直接算。 2.LCM(a,b)=∏ (pi^max(ai,bi)),把a,b 质因数分解 分别表示成∏ (pi^ai)和∏ (pi^bi)的形式,如果a或b中一个有质因子p一个没有,则没有质因子p的指数设成0,这样就能保证表示的一致性。这样就能把最小公倍数以质因数分解的形式表示了。还原成大数可以用一个数组表示,数组中每个数占4-5位。
这里有一个细节要注意,由于最大公倍数是由数组表示的,在分子和分母约分的时候会很困难,所以我们求(2/t1-2/ti)的时候先对分子和分母进行约分,这样就能够保证最后分子分母互质。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 int prime[5000],head,Div[5000]; 5 bool isprime[10005]; 6 void getPrime(){ 7 head=0; 8 memset(isprime,0,sizeof(isprime)); 9 for(int i=2;i<10000;i++)if(!isprime[i]){ 10 prime[head++]=i; 11 for(int j=2*i;j<10000;j+=i){ 12 isprime[j]=true; 13 } 14 } 15 } 16 int gcd(int a,int b){ 17 if(b==0){ 18 return a; 19 } 20 return gcd(b,a%b); 21 } 22 int lcm(int a,int b){ 23 return a*b/gcd(a,b); 24 } 25 const int MAXNUM=1005; 26 int d[MAXNUM],den[MAXNUM]; 27 int main(){ 28 int n,i,j,k,f,numerator; 29 getPrime(); 30 while(~scanf("%d",&n)){ 31 numerator=0; 32 memset(Div,0,sizeof(Div)); 33 memset(den,0,sizeof(den)); 34 den[0]=1; 35 for(i=0;i<n;i++){ 36 scanf("%d",&d[i]); 37 } 38 for(i=1;i<n;i++){ 39 if(d[i]==d[0])continue; 40 int LCM=lcm(d[i],d[0]); //求分母 41 f=2*(int)fabs(LCM/d[i]-LCM/d[0]); //求分子 42 int temp=gcd(LCM,f); 43 LCM/=temp; //分子分母约分 44 f/=temp; 45 numerator=gcd(numerator,f); //求最大公约数 46 for(j=0;j<head;j++){ //分解质因数 47 int t=0; 48 while(LCM%prime[j]==0){ 49 t++;LCM/=prime[j]; 50 } 51 if(Div[j]<t)Div[j]=t; //记录较大的指数 52 if(LCM==1)break; 53 } 54 } 55 for(i=0;i<head;i++){ 56 for(j=0;j<Div[i];j++){ 57 int temp=0; 58 for(k=0;k<MAXNUM;k++){ //大数表示 59 den[k]=den[k]*prime[i]+temp; 60 temp=den[k]/10000; 61 den[k]%=10000; 62 63 } 64 } 65 } 66 for(i=MAXNUM-1;i>=0;i--)if(den[i]!=0)break; 67 printf("%d",den[i]); 68 while(i>0)printf("%04d",den[--i]); //由于表示的4位数可能有前头0,所以强制输出4位 69 printf(" %d\n",numerator); 70 } 71 return 0; 72 }