📅  最后修改于: 2023-12-03 15:28:32.436000             🧑  作者: Mango
本文讨论一道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 ...```。