1674. 使数组互补的最少操作次数

LeetCode 1674:使数组互补最少操作次数

目录

LeetCode 1674 | 使数组互补的最少操作次数

题目描述

题目示例

解题分析

关键思路

差分数组 + 区间更新的巧妙解法

代码实现

复杂度分析

示例说明

总结与思考


LeetCode 1674 | 使数组互补的最少操作次数

题目描述

给你一个长度为偶数 n 的整数数组 nums 和一个整数 limit。你可以对数组执行若干次操作,每次操作可以将数组中的任意元素替换成 [1, limit] 范围内的另一个整数。

我们称数组是 互补的,如果对于所有的下标 i(0-based),满足:

nums[i] + nums[n - 1 - i] == 同一个定值

也就是说,对称位置的元素对之和都是同一个数字。

请你返回使数组互补的 最少操作次数


题目示例

举个例子:

输入:
nums = [1, 2, 3, 4]
limit = 4

输出:1

解释:
原数组对称和分别是 nums[0]+nums[3] = 1+4=5,nums[1]+nums[2] = 2+3=5,
已经相等,替换次数为0。
但是如果是 nums = [1, 2, 2, 4],要使两对和相等,最少替换次数为1,
例如将 nums[2] 替换成 3,使数组变为 [1, 2, 3, 4],两对和都为5。

解题分析

题目要求让对称位置的元素对 (nums[i], nums[n-1-i]) 的和相同,我们可以将数组划分为 n/2 个对。

对每对 (a, b),我们希望替换操作尽可能少,使它们的和达到某个目标值 x。问题的关键:

  • 目标和 x 是一个待选的值,我们需要找到使总替换次数最小的 x
  • 替换操作中,每个元素可以替换为 [1, limit] 之间的任意值。
  • 替换次数对每对 (a,b) 最多为2次(替换两个元素)。

关键思路

对每对 (a,b),替换次数依据目标和 x 有三种情况:

  1. 0 次替换:如果 a + b == x,不需要任何替换。
  2. 1 次替换:如果可以通过替换其中一个元素,使和为 x,例如替换 ax - b,且 x - b[1, limit] 内。
  3. 2 次替换:否则,必须替换两个元素。

这意味着,每个对 (a,b) 对目标和 x 的替换次数是:

  • 0,若 x == a+b
  • 1,若 x[1 + min(a,b), limit + max(a,b)] 范围内但不等于 a+b
  • 2,其他情况

差分数组 + 区间更新的巧妙解法

因为 x 的范围是 [2, 2*limit],我们可以统计对每个可能的目标和 x 替换总次数。

对每对 (a,b),替换次数随 x 的变化形态如下:

x范围

替换次数

x == a + b

0

x ∈ [1 + min(a,b), limit + max(a,b)]

x != a+b

1

其他

2

用差分数组表示替换次数变化:

  • 初始全部为 2 次(全部替换)
  • 对区间 [low, high] = [1 + min(a,b), limit + max(a,b)] 替换次数减少 1
  • 对单点 a+b 替换次数再减少 1

利用差分数组对区间和点进行加减标记,最后对差分数组求前缀和即可得到每个和 x 的总替换次数。


代码实现

from typing import List

class Solution:
    def minMoves(self, nums: List[int], limit: int) -> int:
        n = len(nums)
        change = [0] * (2 * limit + 2)
        
        for i in range(n // 2):
            a = nums[i]
            b = nums[n - 1 - i]
            
            low = 1 + min(a, b)
            high = limit + max(a, b)
            sum_ab = a + b
            
            # 初始替换次数为 2
            change[2] += 2
            
            # 区间内替换次数减少 1
            change[low] -= 1
            change[high + 1] += 1
            
            # 和为 sum_ab 替换次数再减少 1
            change[sum_ab] -= 1
            change[sum_ab + 1] += 1
        
        # 计算前缀和,得到每个和 x 的总替换次数
        for i in range(2, 2 * limit + 1):
            change[i] += change[i - 1]
        
        # 返回最小替换次数
        return min(change[2:2*limit+1])

复杂度分析

  • 时间复杂度:O(n + limit),其中 n 是数组长度,limit 是元素最大值。
  • 空间复杂度:O(limit),用来存储差分数组。

示例说明

nums = [1, 2, 2, 4]limit = 4 为例:

  • 对称对是 (1,4)(2,2)
  • (1,4) 的和是 5,low = 1 + 1 = 2high = 4 + 4 = 8
  • (2,2) 的和是 4,low = 1 + 2 = 3high = 4 + 2 = 6

对于所有可能和 x ∈ [2, 8],计算替换次数:

  • 目标和为5:
    • (1,4) 替换次数为0(和为5)
    • (2,2) 替换次数为1(需要替换一个元素把4变为5)
    • 总计 1 次替换。
  • 目标和为4:
    • (1,4) 替换次数为1(替换一个元素)
    • (2,2) 替换次数为0
    • 总计 1 次替换。

取最小替换次数为 1,和我们的直观分析一致。


总结与思考

  • 这题的难点在于找到如何快速计算所有可能目标和 x 对应的替换次数,而不是对每个 x 都遍历所有对。
  • 差分数组(区间更新)是实现这一点的关键技巧,大幅降低时间复杂度。
  • 该方法也体现了区间计数与前缀和思想在竞赛算法中的典型应用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值