您的位置:首页 > 其它

poj 3101 Astronomy (GCD LCM 高精度)

2012-07-30 11:38 381 查看
题目链接:http://poj.org/problem?id=3101

大致题意:一个恒星系统中有n(2 ≤ n ≤ 1 000)颗行星,告诉你它们的运转周期Ti(1 ≤ ti ≤ 10 000),求他们能够同线的最小周期,表示形式是:分子 分母

Time Limit: 2000MSMemory 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)的时候先对分子和分母进行约分,这样就能够保证最后分子分母互质。

#include<cstdio>
#include<cstring>
#include<cmath>
int prime[5000],head,Div[5000];
bool isprime[10005];
void getPrime(){
head=0;
memset(isprime,0,sizeof(isprime));
for(int i=2;i<10000;i++)if(!isprime[i]){
prime[head++]=i;
for(int j=2*i;j<10000;j+=i){
isprime[j]=true;
}
}
}
int gcd(int a,int b){
if(b==0){
return a;
}
return gcd(b,a%b);
}
int lcm(int a,int b){
return a*b/gcd(a,b);
}
const int MAXNUM=1005;
int d[MAXNUM],den[MAXNUM];
int main(){
int n,i,j,k,f,numerator;
getPrime();
while(~scanf("%d",&n)){
numerator=0;
memset(Div,0,sizeof(Div));
memset(den,0,sizeof(den));
den[0]=1;
for(i=0;i<n;i++){
scanf("%d",&d[i]);
}
for(i=1;i<n;i++){
if(d[i]==d[0])continue;
int LCM=lcm(d[i],d[0]);  //求分母
f=2*(int)fabs(LCM/d[i]-LCM/d[0]);  //求分子
int temp=gcd(LCM,f);
LCM/=temp;  //分子分母约分
f/=temp;
numerator=gcd(numerator,f);  //求最大公约数
for(j=0;j<head;j++){  //分解质因数
int t=0;
while(LCM%prime[j]==0){
t++;LCM/=prime[j];
}
if(Div[j]<t)Div[j]=t;  //记录较大的指数
if(LCM==1)break;
}
}
for(i=0;i<head;i++){
for(j=0;j<Div[i];j++){
int temp=0;
for(k=0;k<MAXNUM;k++){  //大数表示
den[k]=den[k]*prime[i]+temp; 
temp=den[k]/10000;
den[k]%=10000;

}
}
}
for(i=MAXNUM-1;i>=0;i--)if(den[i]!=0)break;
printf("%d",den[i]);
while(i>0)printf("%04d",den[--i]);  //由于表示的4位数可能有前头0,所以强制输出4位
printf(" %d\n",numerator);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: