📌  相关文章
📜  重新排列数组以获得前缀 GCD 串联的最大可能值(1)

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

重新排列数组以获得前缀 GCD 串联的最大可能值

问题描述

给定一个整数数组 nums,你可以对它进行任意次数的重排,使得重排后的数组可以首尾相连,形成一个长度为 n 的环。你的目标是最大化所有 $0 \leq i < n$ 的 gcd(nums[i], nums[(i+1) % n]),即求出重排后数组能够得到的最大可能值。

解决方案
思路

一个显然的结论是,对于一个长度为 n 的环,任意两个相邻元素的 gcd 一定是这个环的周长 k 的因数。因此,对于数组 nums,我们可以枚举其对应的周长 k,然后在所有周长为 k 的情况中寻找最大的 gcd 值。

具体来说,对于给定的周长 k,我们可以将 nums 划分为 n/k 组,每组包含 k 个元素。如果某一组中存在 0,则该组贡献的 gcd 值为 0,否则该组贡献的 gcd 值为这 k 个元素的 gcd。这样,我们可以将重排后得到的数组的所有贡献值相加,得到该排列下的总贡献值。枚举所有周长 k,取最大的总贡献值即可。

实现

第一步:预处理出每个数字的因子

对于每个 nums[i],我们可以使用线性筛法预处理出其所有因子,存储在一个数组 factors[i] 中。时间复杂度为 $O(n \log n)$。

def get_factors(num):
    primes = []
    for i in range(2, int(num ** 0.5) + 1):
        if num % i == 0:
            primes.append(i)
            while num % i == 0:
                num //= i
    if num > 1:
        primes.append(num)
    return primes

n = len(nums)
factors = [[] for _ in range(n)]
for i in range(n):
    factors[i] = get_factors(nums[i])

第二步:枚举周长和组合因子

我们可以使用一个列表 group_factors 存储当前组合的 k 个元素的因子集合。具体来说,在枚举周长 k 时,我们先找到 nums 中所有与当前元素 nums[i] 的 gcd 为 k 的元素,然后将其与 nums[i] 分为一组。对于每个组,我们可以将其 k 个元素的因子集合取一个并集,得到 group_factors。如果 group_factors 非空,则当前组对总贡献值的贡献为其 k 个元素的 gcd 以及 k 的乘积。具体实现如下:

max_gcd = 1
for k in range(1, n + 1):
    if n % k != 0:
        continue
    for i in range(n):
        group_factors = set([])
        for j in range(i, i + n, k):
            group_factors = group_factors.union(set(factors[j]))
        if len(group_factors) > 0:
            gcd_val = reduce(gcd, nums[i:i + k])
            max_gcd = max(max_gcd, gcd_val * k)
return max_gcd
时间复杂度

预处理因子的时间复杂度为 $O(n \log n)$,枚举周长的时间复杂度为 $O(n^2)$,因此总时间复杂度为 $O(n^2 \log n)$。在 n 不是很大时可以接受。

完整代码
from functools import reduce
from math import gcd

def get_factors(num):
    primes = []
    for i in range(2, int(num ** 0.5) + 1):
        if num % i == 0:
            primes.append(i)
            while num % i == 0:
                num //= i
    if num > 1:
        primes.append(num)
    return primes

def max_gcd(nums):
    n = len(nums)
    factors = [[] for _ in range(n)]
    for i in range(n):
        factors[i] = get_factors(nums[i])

    max_gcd = 1
    for k in range(1, n + 1):
        if n % k != 0:
            continue
        for i in range(n):
            group_factors = set([])
            for j in range(i, i + n, k):
                group_factors = group_factors.union(set(factors[j]))
            if len(group_factors) > 0:
                gcd_val = reduce(gcd, nums[i:i + k])
                max_gcd = max(max_gcd, gcd_val * k)
    return max_gcd