题目链接
题面请看链接,分析一下题目,题目就是要求从一等比数列的某几项中得知,这个等比数列的最大公比。
看到题目显然是和gcd有关的,这里介绍两种方法,一种是从直觉而来的,一种是参照网上题解,自己推导而出的。
等比数列必然有一个首项,显然要先将等比数列排序之后两两相除,以此获得一个没有首项的序列。这里有一点需要注意,就是题目中有可能会出现两个一样的数字,这个时候相除答案会是1,但是1是所有正整数的最小公因数 ,显然不是我们要找的数字,所以读入数据的时候需要去重。这里我是用set去重实现的。
之后,获得了相邻两项的商。本来做到这里,我天真的以为只要对这些商求最大公因数就行了,事实上如果存在1、8、80这样的序列,求商后得8、10,如果简单得求最大公因数会得到错误答案8。
一开始我也没考虑到,因为如果公比为8,就无法从序列中得到由8变为80的情况。
这是为什么?本质上是因为没有没有考虑到公比之间的变化,那如何考虑公比之间的变化。以上面的数据举例,如果存在q,则必然存在 q n 1 = 8 q^{n1}=8 qn1=8, q n 2 = 10 q^{n2}=10 qn2=10,如果考虑n1,n2的存在性情况,也就是说需要满足 q n 2 − n 1 = 4 5 q^{n2-n1}=\frac{4}{5} qn2−n1=54,其中n2-n1属于整数。相当于对商数列求最大公因数的时候,多了这一个条件就足够。
所以只需要在获得商数列的基础上,增加这些条件即可。这些条件的获得,直接通过对商数列的排序,然后对相邻的商进行相除即可。获得条件后,其实条件和商数列形式上是完全一样的,也就是这些数值需要出现在整个数列中。
所以我们通过在商数列后面追加这些数值即可。
下面放上通过的python代码
def gcd(a, b):
if b == 0:
return a
return gcd(b, a % b)
def lgcd(a, b):
return [gcd(a[0],b[0]),gcd(a[1],b[1])]
while True:
try:
n = int(input())
l = list(set(map(int, input().split())))
l.sort()
n = len(l)
tl = []
for i in range(n - 1):
g = gcd(l[i], l[i + 1])
tl.append((l[i + 1] // g, l[i] // g))
tl=list(set(tl))
tl.sort(key=lambda x:x[0]/x[1])
n=len(tl)
for i in range(n-1):
tl.append((tl[i+1][0]/tl[i][0],tl[i+1][1]/tl[i][1]))
g = tl[0]
for i in tl:
g = lgcd(g, i)
print('%d/%d' % (g[0], g[1]))
except:
break
辗转相除法 想来大家都是知道的,甚至能闭着眼睛打出来,但是大部分人并没有看推导,所以不知其本质,笔者也是如此。
回去看了一下辗转相除法之后,顿时灵感迸发。
首先辗转相除法的代码是
def gcd(a,b):
if b==0:
return a
return gcd(b,a%b)
意思也就是gcd(a,b)=gcd(b,a%b),那么如何证明?
设a%b=c,那么 b ∗ n + c = a b*n+c=a b∗n+c=a, , c = a − b ∗ n c=a-b*n c=a−b∗n,已知a和b存在最大公约数m,那么右边一定可以被m整除,所以c一定能被m整除。
现在假设存在约数(注意不是最大公约数)d使得b%d= =0和(a%b)%d= =0同时成立,那么a%b=kd,a=nb+kd,右边能被d整除,所以a能被b整除。得到结论,如果存在d使得b%d= =0和(a%b)%d==0同时成立,那么a%d= =0和b%d= =0也成立。
根据上述推论,设a%b=c,
通过上两条性质,可知a,b公因子和b,c公因子完全相同。故最大公因数也相同。
至此得证gcd(a,b)=gcd(b,a%b)
如果我们假设a>b,那么每次递归gcd时,第一个参数一定在变小,而第二个参数因为%b所以,第二个参数的最大值也在变小啊,问题规模一直在变小。直到能够处理为止,大致思路是如此,具体细节不再称述。
现在是已知 q a q^{a} qa和 q b q^b qb如何求q?设q=gcq(q^a, q^b),q是其最大公比,借鉴上面的思想,找到一个相同答案但是规模更小的递归问题即可。显然,两者相除得到 q a − b q^{a-b} qa−b,如果对其和 q b q^b qb求gcq,问题规模变小了,但是答案仍是一样。
递归出口在哪?显然如果一直递归,参数一直都会是q的幂次,除非在规模很小的时候,两个参数相等了,也就是下一次递归时,一个参数为1,那么递归结束。返回非一参数即可。注意此处根据题意假设,公比大于1,如想得到更一般的情况,还需更多的考虑。
转换成代码也就是
def gcq(a, b):
if a < b:
t = a;a = b;b = t
if b == 1:
return a
return gcq(b, a // b)
上面是q为整数版本,此题是分数,存在list里面分子在0位置,分母在1位置
def fgcq(a, b):
# print(a,b)
if a[0] / a[1] < b[0] / b[1]:
t = a;a = b;b = t
if b[0] == 1 and b[1] == 1:
return a
return fgcq(b, [a[0] // b[0], a[1] // b[1]])
最后完整代码
def gcq(a,b):
if a<b:
t=a;a=b;b=t;
if b==1:
return a
return gcq(b,a//b)
def fgcq(a,b):
#print(a,b)
if a[0]/a[1]<b[0]/b[1]:
t=a;a=b;b=t;
if b[0]==1 and b[1]==1:
return a
return fgcq(b,[a[0]//b[0],a[1]//b[1]])
def gcd(a,b):
if b==0:
return a
return gcd(b,a%b)
while True:
try:
n=int(input())
l=list(set(map(int,input().split())))
l.sort()
n=len(l)
tl=[]
for i in range(n-1):
g=gcd(l[i],l[i+1])
tl.append([l[i+1]//g,l[i]//g])
g=tl[0]
for i in tl:
g=fgcq(g,i)
print('%d/%d'%(g[0],g[1]))
except:
break