📅  最后修改于: 2023-12-03 14:55:20.628000             🧑  作者: Mango
给定一个长度为n的整数数组,你需要最小化通过以下任一操作使该数组的每个元素等于其索引值的总次数:
请你输出最小操作次数。
首先遍历整个数组,找到所有满足 $a_i = i$ 的元素,并将其作为 "匹配元素"。如果存在 "匹配元素",那么将它们从数组中剔除,并在接下来的操作中不再考虑这些元素。
接下来,我们需要将剩余的元素尽可能地变为其对应的下标。我们可以考虑把第 $i$ 个元素 $a_i$ 移动到位置 $i$,需要进行的操作数为 $|a_i - i|$,因为该元素需要向左或向右移动 $|a_i - i|$ 的距离。但是,这个操作可能会导致其他元素的位置变化,进而影响到其他元素的操作次数。因此,我们需要找到一种方式,使得每个元素只发生一次移动,并且所有移动都能够顺利地执行。
考虑将所有元素按照下标升序排列,并对于每个元素 $a_k$,把它移动到位置 $k$ 上。这样,我们可以保证元素的移动是前后不会相互干扰的。具体地说,若 $a_k \geq k$,我们可以先把它移动到 $\lfloor\frac{a_k + k}{2}\rfloor$,再移动到 $k$;若 $a_k<k$,我们可以先把它移动到 $\lceil \frac{a_k+k}{2}\rceil$,再移动到 $k$。这样,我们就得到了一组可行的操作,对于每个元素 $a_k$,它需要进行的操作次数为 $|a_k - k| + \max(a_k, k) - \min(a_k, k)$。
综上,我们可以计算出将 "匹配元素" 剔除后,剩余元素变为对应下标的最小操作次数。同时,我们还需要考虑将 "匹配元素" 从数组中剔除和删除元素的操作次数。因此,最终的答案为剔除 "匹配元素" 和删除元素的操作次数加上剩余元素变为对应下标的最小操作次数。
假设数组长度为 $n$,则排序的时间复杂度为 $O(n\log n)$,对于每个元素 $a_k$,找到它需要移动的位置的时间复杂度为 $O(1)$,因此总时间复杂度为 $O(n\log n)$。
def minimizeOperations(n: int, a: List[int]) -> int:
# 找到所有匹配元素
matches = []
for i in range(n):
if a[i] == i:
matches.append(i)
# 将匹配元素从原数组中删除
for i in matches:
a[i] = -1
a = [i for i in a if i != -1]
# 对于剩余元素,计算需要移动的次数
a.sort()
ans = sum([abs(a[i] - i) for i in range(len(a))])
# 添加对于匹配元素和删除元素的操作次数
ans += len(matches) + n - len(a)
return ans