【python】又又又又参加笔试了--- 经典gcd问题

4 经典gcd问题

题目来源:https://vjudge.net/problem/UVA-1642
参考[1]:https://www.cnblogs.com/zhuanzhuruyi/p/6701373.html?utm_source=itdadao&utm_medium=referral

题目:给你n(n<=100000)个正整数,求一个连续子序列使序列的所有元素的最大公约数与个数乘积最大
输入:
5
30 60 20 20 20
输出:
80

gcd有三个性质:

  1. gcd(a, a) = a
  2. gcd(a, b) = gcd(b, a mod b)
  3. gcd(a, b, c) = gcd( gcd(a, b), c)
#求两个数的最大公约数
def gcd(a,b):
    if b == 0:
        return a
    else:
        return gcd(b,a%b)
  • 用一个列表中的列表g,存储以arr[i]结尾的长度为l+1的gcd值
  • 根据gcd的性质给出递推:
  • g[i][l] = ( g[i-1][l-1], arr[i] )
  • 复杂度是O(n^2)

eg: 对于数列a b c d e
列表g中有5个列表, 第i个列表中存储i+1个gcd值,
第0个列表为[a]
第1个列表为[b, gcd(a, b)]
第2个列表为[c, gcd(c, b), gcd(c, b, a)]
第3个列表为[d, gcd(d, c), gcd(d, c, b), gcd(d, c, b, a)]
第四个列表为~~~~

# 传入数列及长度n
def solvegcd(arr,n):
    #g[i][l]存储以arr[i]结尾的长度为l+1的gcd值,0<=l<=i
    g = [] 
    g.append([arr[0]])
    maxgl = arr[0]
    for i in range(1,n):
        #g[i][0]即长度为1时的gcd就是arr[i]自己
        g.append([arr[i]])
        #print(g)
        for l in range(1,i+1):
            il = gcd(g[i-1][l-1],arr[i])
            #print(arr[i],'长度为:',l+1,il)
            g[i].append(il)          
            maxgl = max(g[i][l]*(l+1),maxgl)
            #print(maxgl)
a = [30,60,20,20,20]               
solvegcd(a,5)                
输出:
80
  • 上面这个复杂度太高了,参考[1]的方法可以改进:

由性质3知道 k k k个数与 k + 1 k+1 k+1个数的最大公约数要么相等,要么减小并且减小至少一半。

a a a为当前因子,因子最小为1,那么衰减速度为 l o g ( a ) log(a) log(a)。 长度为n的序列所有子串gcd的总种类数最多只有 n ⋅ l o g ( a ) n·log(a) nlog(a)个。

用动态规划的思想:遍历每个数,计算以这个数为结束的gcd,通过前一次计算的最多log(a)个gcd计算出此时也是最多log(a’)个gcd

  • 也就是说上面的列表g中某个列表的值为1时,后面的gcd都为1,不必再计算(不知道我这样理解对不对) 当数据很大时,优越性可以显现出来。

留个思路坑:

  • 输入序列arr,按l查找:
  • l=1,gcd最大的就是arr最大值arr[i]
  • l=2,只找含arr[i]的连续子列gcd值,
    g1 = gcd( arr[i-1], arr[i] ), g2 = (arr[i], arr[i+1]),
    g1>g2: M = i, m = M-(l-1)
    g1<g2:m = i, M = i+(l-1)
    g=max(g1, g2)
  • l=3,g1 = gcd(arr[m-1], g),g2 = gcd(g, arr[M+1l])
    g1>g2: m=m-1, M=m+l-1
    g1<g2: M=M+1, m=M-(l-1)
    g=max(g1, g2)
  • l=4 依次推演下去~~
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值