leetcode 3224. 使差值相等的最少数组改动次数

题目链接:3224. 使差值相等的最少数组改动次数

题目:

给你一个长度为 n 的整数数组 nums ,n 是偶数 ,同时给你一个整数 k 。

你可以对数组进行一些操作。每次操作中,你可以将数组中任一元素替换为 0 到 k 之间的任一整数。

执行完所有操作以后,你需要确保最后得到的数组满足以下条件:

存在一个整数 X ,满足对于所有的 (0 <= i < n) 都有 abs(a[i] - a[n - i - 1]) = X 。
请你返回满足以上条件最少修改次数。

提示:

2 <= n == nums.length <= 105

n 是偶数

0 <= nums[i] <= k <= 105

题解:

方法一:暴力解法

直接枚举 X 的取值,然后计算每个枚举值对应所需修改的次数,然后选出里面最小的即可。

这里的第一个问题是 X 的取值范围如何确定。从题目的提示内容可知数组中的数在 0-k 之间,又因为 数组中的数字可以修改为 0-k之间的任意数,所以直接上 X 的取值范围为 0 <= X <= k

第二个问题是如何让数组中对称位置的两个数字在修改之后差值为 X,这里直接枚举两个数字可以修改的所有值,如果修改前后的数值不同则记录为1次修改,否则不记录为修改。

代码实现:

def minChanges(nums, k):
    n = len(nums)
    change_count = [0] * (k + 1)

    # 遍历前半部分
    for i in range(n // 2):
        a, b = nums[i], nums[n - i - 1]
        temp = [float('inf')] * (k + 1)  # 临时数组,用于计算当前的最小修改次数

        # 遍历每个可能的 X
        for x in range(k + 1):
            # 当前 |a - b| 与目标 X 的差异
            for v1 in range(k + 1):  # 枚举将 a 修改为 v1
                for v2 in range(k + 1):  # 枚举将 b 修改为 v2
                    if abs(v1 - v2) == x:  # 如果调整后符合要求
                        cost = (v1 != a) + (v2 != b)  # 计算修改次数
                        temp[x] = min(temp[x], change_count[x] + cost)

        # 更新最小修改次数
        change_count = temp

    return min(change_count)

方法二:差分数组

注意到每一对数不会互相影响,所以可以把每一对数分开考虑。
设一对数中,一个是 x x x,一个是 y y y,那么改掉其中一个数可以获得的最大差值,肯定是把另一个数改成 0 或 k,即最大差值:

m = m a x ( x , k − x , y , k − y ) m=max(x, k-x, y, k-y) m=max(x,kx,y,ky)

参考下表:

x y 差值
x 0 x
x k k-x
0 y y
k y k-y

改掉两个数,那就可以获得从 0 到 k 里的任意差值。

所以对于这一对数来说, X = ∣ x − y ∣ X=|x-y| X=xy 时不需要操作, 0 ≤ X < ∣ x − y ∣ 0 \leq X < |x-y| 0X<xy 以及 ∣ x − y ∣ < X ≤ m |x-y| < X \leq m xy<Xm 的时候需要一次操作, X > m X>m X>m 的时候需要两次操作。

枚举所有数对,用差分数组维护 X X X 的某个取值需要几次操作即可。复杂度 O ( n + k ) O(n+k) O(n+k)

  • 我们令 d = a b s ( n u m s [ i ] − n u m s [ j ] ) d=abs(nums[i]-nums[j]) d=abs(nums[i]nums[j]),假如最后的这个 0 ≤ X < ∣ x − y ∣ 0 \leq X < |x-y| 0X<xy,那么就可以通过一次操作完成。
  • 操作一次能达到的最大差值是多少呢?这个就是上面说的肯定是把另一个数改成 0 或 k。即最大差值为 m = m a x ( x , k − x , y , k − y ) m=max(x, k-x, y, k-y) m=max(x,kx,y,ky),那么就是 ∣ x − y ∣ < X ≤ m |x-y| < X \leq m xy<Xm 时需要一次操作。
  • 剩下就是 X > m X > m X>m 时需要两次操作。

因为是对区间内的值进行统一的加一个常数的操作,如果一个一个操作的话时间复杂度为 O ( n ) O(n) O(n),所以使用了差分数组,这样可以将时间复杂度降为 O ( 1 ) O(1) O(1)

from typing import List

class Solution:
    def minChanges(self, nums: List[int], k: int) -> int:
        n = len(nums)
        f = [0] * (k + 2)
        
        i, j = 0, n - 1
        while i < j:
            d = abs(nums[i] - nums[j])
            ma = max(nums[i], k - nums[i], nums[j], k - nums[j])
            
            # 0 <= x < d 时需要一次操作,如果没有用差分数组的话就成了 cnt[0]+1,cnt[1]+1,···,cnt[d]+1
            f[0] += 1
            f[d] -= 1
            
            # d < x <= ma 时需要一次操作
            f[d + 1] += 1
            f[ma + 1] -= 1
            
            # x > ma 时需要两次操作
            f[ma + 1] += 2
            
            i += 1
            j -= 1
        
        ans = f[0]
        for i in range(1, k + 2):
            f[i] += f[i - 1]
            ans = min(ans, f[i])
        
        return ans

参考1:3224. 使差值相等的最少数组改动次数
参考2:前缀和&差分

上一篇:物联网入门-Arduino的下载与配置教程(以ESP32为例)-2024


下一篇:GitHub Actions 自动部署前端项目到阿里云 OSS-二、准备工作