题意:给定 n 个行星的运行周期 T1,T2,...,Tn,求它们两次共线的最短时间间隔 ;
分析:看输入全是整数,先写了一发整数的最小公倍数(果断WA),设 t1为行星 1 和 2 两次共线的最短时间间隔,则它应该满足 转换一下就变成了
则最终答案应该是 t1~tn-1 的最小公倍数,那么分数的最小公倍数即 分子的最小公倍数 / 分母的最大公约数 ,那分子的最小公倍数肯定存不下,所以得用数组存,因为要求最终是不可再约的分数,所以分子先存素数分解形式,细节比较多,看下面的代码;
代码:
#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 10000+10;
int T[N];
int p[N],cnt;
int tim[N]={0};
int ans[N]={0};
void init(){ //素数打表
p[1]=2,p[2]=3,cnt=2;
for(int i=5;i<=10000;i+=2){
int ok=1;
for(int k=1;k<=cnt;k++){
if(i%p[k]==0){
ok=0;
break;
}
}
if(ok) p[++cnt]=i;
}
}
ll gcd(ll a,ll b){
if(a>b) swap(a,b);
if(a==0) return b;
if(b%a==0) return a;
return gcd(b%a,a);
}
int main()
{
init();
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>T[i];
int fm=0;
for(int i=2;i<=n;i++){
if(T[1]==T[i]) continue;
int x=T[1]*T[i]; //当前分子
int y=abs(T[1]-T[i]); //当前分母
int g=gcd(x,y); //约分
x/=g,y/=g;
fm=gcd(fm,y); //更新分母的最大公约数
for(int k=1;k<=cnt;k++){ //将当前分子素数分解并更新
if(x<p[k]) break;
if(x%p[k]==0){
int cnt=0;
while(x%p[k]==0) cnt++,x/=p[k];
tim[k]=max(tim[k],cnt);
}
}
}
fm*=2; //分母形式是2*(T1-T2)的
for(int i=1,cop=fm;i<=cnt;i++){ //再对总体分子的最小公倍数和分母的最大公约数约分
if(cop<p[i]) break;
if(cop%p[i]==0){
int cnt=0;
while(cop%p[i]==0) cnt++,cop/=p[i];
if(cnt<=tim[i]){
for(int z=0;z<cnt;z++) fm/=p[i];
tim[i]-=cnt;
}
else{
for(int z=0;z<tim[i];z++) fm/=p[i];
tim[i]=0;
}
}
}
int tot=0;
ans[0]=1;
for(int i=1;i<=cnt;i++){ //把分子的素数分解形式转化成数组形式,一个元素存四位数
if(tim[i]){
while(tim[i]--){
for(int k=0;k<=tot;k++){
ans[k]=ans[k]*p[i];
}
for(int k=0;k<=tot;k++){
if(ans[k]>=10000){
ans[k+1]+=ans[k]/10000,ans[k]%=10000;
if(k==tot) tot++;
}
}
}
}
}
printf("%d",ans[tot--]);
for(int i=tot;i>=0;i--) printf("%04d",ans[i]);
printf(" %d",fm);
}