POJ - 3101 (Astronomy)

题意:给定 n 个行星的运行周期 T1,T2,...,Tn,求它们两次共线的最短时间间隔 ;

 

分析:看输入全是整数,先写了一发整数的最小公倍数(果断WA),设 t1为行星 1 和 2 两次共线的最短时间间隔,则它应该满足 t1*(\frac{2\pi }{T1}-\frac{2\pi }{T2})=\pm \pi  转换一下就变成了 t1=\left | \frac{T1*T2}{2*(T1-T2)} \right |    则最终答案应该是 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);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值