方法一、数学
==注意:输入数据会有重复的==
思路于分析:
我们首先叫所有的数据进行排序,然后求出相邻两个比值。
设排序后的序列:$a_0,a_1,…,a_{n-1}$ 。
两两比值序列为:$q_1,q_2,…,q_{n-1}$ 。 其中 $q_i = a_{i+1}/a_i$。
显然,这题答案应该是序列 $q[n]$ 的所有最大公约数。
==当现在的难点在于 $q_i$ 是分数而非整数!!==
为了解决这个问题,我们需要利用到两个数组 $a[n]和b[n],q_i=a[i]/b[i]$ 。
因为这题保证数据有解,假设最终答案为 $a/b$ 。那么序列 $q[n]$ 可以表示为:
$$
q_i=(\frac{a}{b})^{\alpha_i} ,可以知道序列\alpha[n]序列是递增的(判重之后)。
$$
现在我们有序列 $\alpha[n]$ ,我们所求就转换为求出序列 $\alpha[n]$ 的最大公约数。
但问题回来了,我们其实并不知道 $最终答案 a/b $ ,而要求出它,我们只能利用序列 $q[n]$ 。
那如何求呢??
- 答案 $a/b$ 不一定是序列 $q[n]$ 中的最小值:
例如:现有序列$q[3]:(3/2)^2,(3/2)^3,(3/2)^4$ 显然此时答案 $a/b=3/2 ~ 而非 (3/2)^2$ 。
- 我们可以分开处理序列$a[n]~ 以及 b[n]$ , 为什么可以分别处理呢?
因为 $a[i]$ 和 $b[i]$ 有相同次幂数,分别处理并不影响答案,反而处理起来更加方便有效。
- 下面就是重点了,如何求出 $a/b$ 呢?
根据上面的分析,我们知道$a/b$ 就是序列 $q_i,q_2,…,q_n$ 的最大公约数。
不能用“辗转相除法”求:
相除法求出来得可能是$a/b$ 的开几次根,而非 $a/b$。
例如序列:$(3/2)^2,(3/2)^4,(3/2)^6$ 使用相除法求出的会是 $3/2$ 而非答案 $(3/2)^2$。
所以我们使用“辗转相减法”
相减法的特性,其最后求出来的会是 $a/b$ 而非其开几次根。
如何使用“相减法”
原版的代码如下:
int gcd_sub(int a, int b)
{
if(a == b) return a;
if(a > b) a -= b;
else b -= a;
return gcd_sub(a, b);
}
显然,不能这样使用,因为这本质还是求 $【最大公约数】$ 而非我们想要的答案 $a/b$ 。
我们要求的其实是 $a/b=(r/t)^{某某}$ ,而$q_i =(r/t)^{某某某}$ ,所以相减法可以稍加变形,如下:
int gcd_sub(int a, int b)
{
if(a < b) swap(a, b);
if(b == 1) return a;
return gcd_sub(b, a / b);
}
Y总代码:
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 110;
int n;
LL a[N], b[N], x[N];
LL gcd(LL a, LL b)
{
return b ? gcd(b, a % b) : a;
}
LL gcd_sub(LL a, LL b)
{
if (a < b) swap(a, b);
if (b == 1) return a;
return gcd_sub(b, a / b);
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ ) cin >> x[i];
sort(x, x + n);
int cnt = 0;
for (int i = 1; i < n; i ++ )
if (x[i] != x[i - 1])
{
LL d = gcd(x[i], x[0]);
a[cnt] = x[i] / d;
b[cnt] = x[0] / d;
cnt ++ ;
}
LL up = a[0], down = b[0];
for (int i = 1; i < cnt; i ++ )
{
up = gcd_sub(up, a[i]);
down = gcd_sub(down, b[i]);
}
cout << up << '/' << down << endl;
return 0;
}
真不错
好详细
博主写的好好!终于明白为啥可以分开处理分子分母了!但是这道题辗转相减变形后返回b==1,返回a是为啥啊?
把条件改成
b==a
也可以AC。b==1
就是需要下面多进行一步gcd_sub(b,a/b)
改成这个真的可以AC!
a==b
就好理解多啦!但是我还是不太理解为什么b==1
然后返回a
是啥意思?不过还是太谢谢你拉!我的理解就是,
b==1
的时候,说明上一轮gcd_sub(a,b)
里面的a和b相等,此时的a和b就是最大公约数了,但是因为判断条件是b==1
,会继续调用gcd_sub(b,a/b)
,这个时候传入两个参数变成新的a和b。所以b==1后返回的a就是调用的时候给的参数b,就是最大公约数好嘞,太感谢了!!!!!
这个写的好
使用相除法求出的会是…这里写反了