📌  相关文章
📜  具有最大可能GCD的大小为k的子序列(1)

📅  最后修改于: 2023-12-03 15:07:06.417000             🧑  作者: Mango

具有最大可能GCD的大小为k的子序列

介绍

这个问题的主要目标是从一组整数中找到最大可能 GCD 为 k 的子序列。

  • GCD (Greatest Common Divisor)就是两个或更多整数的最大公约数。
  • 子序列指从给定序列中选择一系列元素按原来顺序排列而成的序列。
  • K 是给定的整数,需要在给定的序列中找到具有最大可能 GCD 为 K 的子序列。
解决方案

一般情况下,求 GCD 是一个O(N) 的操作,那么我们可以考虑使用某种方法将复杂度降下来。根据定义 GCD 的性质,我们可以把 k 取公因数,然后把原序列的每个数都除以这个公因数,那么我们可以采用欧几里得算法递归求出这个序列的最大公约数,然后再乘回去即可求出原序列 GCD 为 k 的序列。

我们可以使用动态规划去求解,定义状态 dp[i][j] 表示下标从 1 开始的前 i 个元素中最大公约数为 j 的最长子序列长度。

显然,在求解 dp[i][j] 时,我们需要枚举前一个状态 dp[i-1][k] 的所有可能值,然后递归求解 dp[i][gcd(j, k)]。最终的答案为所有 dp[n][k] 中的最大值。

代码
def GCD(a, b):
    if(b==0): return a
    return GCD(b,a%b)

def findMaxSubsequence(n, k, a):
    a = [x // k for x in a]
    MAX = max(a)
    dp = [[0]*(MAX+1) for _ in range(n+1)]
    for i in range(1, n+1):
        for j in range(1, MAX+1):
            for k in range(i):
                if GCD(a[i-1], a[k]) == j:
                    dp[i][j] = max(dp[i][j], dp[k][j]+1)
        # 初值就是1,不需要上面的循环
        if GCD(a[i-1], k) == 1:
            dp[i][1] = max(dp[i][1], 1)
    res = max(dp[n])
    return res

上述代码采用了传统的动态规划方式,时间复杂度是 O(n^3) 的,我们可以利用欧几里得的思想,用辗转相除法进行迭代求解,时间复杂度可以优化到 O(n^2*log(max(a)))。下面是修改后的代码:

def GCD(a, b):
    if(b==0): return a
    return GCD(b,a%b)

def findMaxSubsequence(n, k, a):
    a = [x // k for x in a]
    MAX = max(a)
    dp = [[0]*(MAX+1) for _ in range(n+1)]
    for i in range(1, n+1):
        for j in range(1, MAX+1):
            for k in range(j, MAX+1, j):
                if dp[i-1][k]:
                    dp[i][j] = max(dp[i][j], dp[i-1][k] + (GCD(a[i-1], j) == k))
        if GCD(a[i-1], k) == 1:
            dp[i][1] = max(dp[i][1], 1)
    res = max(dp[n])
    return res
总结

这个问题是一个经典的动态规划问题,可以使用传统的DP思想解决。同时也可以用辗转相除的思想和网上的一些博客提供的一些方法进行优化。在解决此类问题时,我们可以按照以下步骤进行:

  1. 明确问题,理清求解思路。
  2. 设计状态表示,写出状态转移方程式。
  3. 编码实现,验证正确性,考虑优化思路。