代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 110;
ll a[N], fz[N],fm[N];
ll gcd(ll x, ll y)
{
return y ? gcd(y, x % y) : x;
}
//这里之所以弃用辗转相除法的原因,是因为虽然更相减损术效率更低,
//但是它能有效地避免q为其他数的次方得到的数,比如9/4 可以为 2/3的平方
//假设原数列为 a,a*(p/q)^1,a*(p/q)^2,...,a*(p/q)^(n-1)
//假设抽取的数列 b0,b1,...,b(N-1) (从小到大排好序了)
// b1/b0,b2/b0,...,b(N-1)/b0--> (p/q)^x1,(p/q)^x2,...,(p/q)^x(N-1)
// 我们要求的是: (p/q)^k (p/q)>1,所以使k最大,即求(p/q)^x1~(p/q)^x(N-1)的最大公约数,其实就是求x1~x(N-1)的最大公约数
//这里我们使用更相减损术,因为我们没有得到确切的x1~x(N-1)是多少,我们只有(p/q)^x1,(p/q)^x2,...,(p/q)^x(N-1)这些的值
//我们这里要用更相减损术的是指数,所以要让(p/q)^x1,(p/q)^x2,...,(p/q)^x(N-1),两两计算,
//互除,除到结果为1,即x1=x2,此时幂次为0,结果为1,这其实就是y总的思路,再次感叹y总的才华
//把分子分母分别去算,结果是相同的因为,分子分母的幂次是相同的
ll gcd_sub(ll x, ll y)//更相减损
{
if(x < y) swap(x, y);
if(y == 1) return x;//因为是对指数的更相减损术,此时幂次为0,则y为1,x即为最后的需要得到的比例数
return gcd_sub(y, x / y);//因为是对指数的更相减损术 所以用除法
}
int main()
{
int n; cin >> n;
int cnt = 0;
for(int i = 0; i < n; i++) cin >> a[i];
sort(a, a + n);//排序
for(int i = 1; i < n; i ++)
{
if(a[i] != a[i -1])//保证两两不相等
{
ll d = gcd(a[0], a[i]);
fz[cnt]=a[i] / d;//指数的分子
fm[cnt]=a[0] / d;//指数的分母
cnt ++;
}
}
ll up=fz[0], down = fm[0];
for(int i = 1; i < cnt; i ++)
{
up = gcd_sub(up, fz[i]);
down = gcd_sub(down, fm[i]);
}
cout << up <<'/' << down << endl;
return 0;
}
感悟
首先,是对辗转相除法的复习
其次,是对更相减损法的学习,正宗的更相减损法是
int gcd_sub(int x, int y)
{
if(x < y) swap(x, y);
if(x == y) return x;
return (y, x - y);
}
而这是用到了更相减损法的原因是:
想要求的对于n个数来说,在求得每两个数指数的最大公约数的条件下的最大公约数。
所以这里用到的更相减损法是:
ll gcd_sub(ll x, ll y)//更相减损
{
if(x < y) swap(x, y);
if(y == 1) return x;//因为是对指数的更相减损术,此时幂次为0,则y为1,x即为最后的需要得到的比例数
return gcd_sub(y, x / y);//因为是对指数的更相减损术 所以用除法
}
通过这种方法,得到最后的,最简的最大比例系数。而这里没有用辗转相除法的原因是因为需要将每个数分解质因数,然后再对每个质因子的次数求最大公约数,这样写会很麻烦。