📌  相关文章
📜  重新排列数组以最大化数组元素及其索引的GCD之和(1)

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

重新排列数组以最大化数组元素及其索引的GCD之和

介绍

本文讨论一道LeetCode上的中等难度题目:重新排列数组以最大化数组元素及其索引的GCD之和

题目的描述如下:

给你一个长度为n的整数数组nums。重新排列nums得到一个新数组order(下标从0开始计数)。

比方说,nums=[0,1,2,3]可以得到下列24个不同的新数组:

[0,1,2,3],[0,1,3,2],[0,2,1,3],[0,2,3,1],[0,3,1,2],[0,3,2,1], [1,0,2,3],[1,0,3,2],[1,2,0,3],[1,2,3,0],[1,3,0,2],[1,3,2,0], [2,0,1,3],[2,0,3,1],[2,1,0,3],[2,1,3,0],[2,3,0,1],[2,3,1,0], [3,0,1,2],[3,0,2,1],[3,1,0,2],[3,1,2,0],[3,2,0,1],[3,2,1,0]。

请你返回重新排列nums后,结果数组order最大的可能总和。答案保证在32位有符号整数范围内。

思路

题目中需要计算GCD之和,可以考虑使用容斥原理。

我们可以枚举每个数i是多少个数的公约数,注意不能算上自己,设为cnt[i]。

假设i是k个数的公约数,它们分别为$a_1,a_2,...,a_k$,它们在原数组nums中的下标为$idx_1,idx_2,...,idx_k$。

对于一个公约数为i的数i来说,我们需要在新的数组order中将这k个数$a_1,a_2,...,a_k$排列,即确定它们在数组中的位置。在确定它们的位置后,它们在order中的极差就是i。

设一个数组f[i]表示公约数为i的数在order中的极差之和,则f[i]=i*cnt[i]-sigma{j=1 to k}(idx_j),其中sigma{j=1 to k}(idx_j)表示这些数在nums数组中的下标之和。

使用容斥原理,枚举1到数组中最大数maxNum的公约数,用所有公约数的f[i]减去所有它的倍数的f[i],得到的就是新数组order的极差之和。

此时问题就很简单了,我们只需要把nums数组从大到小排序,然后依次求出order数组的每个数即可。

具体操作可见代码。

代码
class Solution:
    def maxSumAfterPartitioning(self, nums: List[int], k: int) -> int:
        n = len(nums)
        # 求出每个数的cnt,即它是多少个数的公约数(不算自己)
        cnt = [0] * (max(nums) + 1)
        for i in range(1, max(nums) + 1):
            for j in range(i*2, max(nums)+1, i):
                cnt[i] -= cnt[j]
            cnt[i] += n - cnt[i] - 1
        
        # 求出f[i]
        f = [0] * (max(nums) + 1)
        for i in range(1, max(nums) + 1):
            for j in range(i, max(nums) + 1, i):
                f[i] += nums.index(j) - (i-1)*k
        ans = 0
        for i in range(1, max(nums) + 1):
            tmp = f[i]
            j = i * 2
            while j <= max(nums):
                tmp -= f[j]
                j += i
            ans += cnt[i] * tmp
        return ans

对于Python的代码,我们可以使用Markdown的代码块语法,将代码片段加入的标记中,即python ...```。