藍橋杯 2016省賽 最大比例 從直覺到輾轉相除法的變化

藍橋杯 2016省賽 最大比例 從直覺到輾轉相除法的變化

題目鏈接

題面請看鏈接,分析一下題目,題目就是要求從一等比數列的某幾項中得知,這個等比數列的最大公比。

看到題目顯然是和gcd有關的,這裏介紹兩種方法,一種是從直覺而來的,一種是參照網上題解,自己推導而出的。

1.直覺題解

        等比數列必然有一個首項,顯然要先將等比數列排序之後兩兩相除,以此獲得一個沒有首項的序列。這裏有一點需要注意,就是題目中有可能會出現兩個一樣的數字,這個時候相除答案會是1,但是1是所有正整數的最小公因數 ,顯然不是我們要找的數字,所以讀入數據的時候需要去重。這裏我是用set去重實現的。

        之後,獲得了相鄰兩項的商。本來做到這裏,我天真的以爲只要對這些商求最大公因數就行了,事實上如果存在1、8、80這樣的序列,求商後得8、10,如果簡單得求最大公因數會得到錯誤答案8。

        一開始我也沒考慮到,因爲如果公比爲8,就無法從序列中得到由8變爲80的情況。

        這是爲什麼?本質上是因爲沒有沒有考慮到公比之間的變化,那如何考慮公比之間的變化。以上面的數據舉例,如果存在q,則必然存在qn1=8q^{n1}=8,qn2=10q^{n2}=10,如果考慮n1,n2的存在性情況,也就是說需要滿足qn2n1=45q^{n2-n1}=\frac{4}{5},其中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,那麼bn+c=ab*n+c=a, , c=abnc=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,

  1. 如果存在d使得b%d= =0和c%d==0同時成立,則a%d= =0和b%d= =0也成立。
  2. 如果存在d使得a%d= =0和b%d= =0成立,則b%d= =0和c%d==0成立

        通過上兩條性質,可知a,b公因子和b,c公因子完全相同。故最大公因數也相同。
至此得證gcd(a,b)=gcd(b,a%b)

        如果我們假設a>b,那麼每次遞歸gcd時,第一個參數一定在變小,而第二個參數因爲%b所以,第二個參數的最大值也在變小啊,問題規模一直在變小。直到能夠處理爲止,大致思路是如此,具體細節不再稱述。

        現在是已知qaq^{a}qbq^b如何求q?設q=gcq(q^a, q^b),q是其最大公比,借鑑上面的思想,找到一個相同答案但是規模更小的遞歸問題即可。顯然,兩者相除得到qabq^{a-b},如果對其和qbq^b求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

	
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章