返回

力扣斩将夺旗:动态规划剑指462:至少移动次数使数组元素相等II

前端

在力扣题海中,有一道极具挑战性的题:462.最少移动次数使数组元素相等II。这道题不仅考察了算法基本功,更检验了对数学和动态规划的灵活运用。

题目重述:
给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数。每次移动可将选定的一个元素加1或减1。假设数组长度最多为10000。

暴力法:
暴力法是最朴素的解法。它枚举所有可能的均值,然后计算每个均值的最小移动次数。具体步骤如下:

  1. 找出数组中的最大值和最小值。
  2. 对于每个可能的均值,计算使所有元素都等于该均值所需的最小移动次数。
  3. 找出所有最小移动次数中的最小值。

动态规划:
动态规划是一种自底向上的解法。它利用了这样一个事实:最小移动次数可以从较小的子问题中计算出来。具体步骤如下:

  1. 定义状态dp[i][j],表示使数组前i个元素都等于j所需的最小移动次数。
  2. 初始化dp[0][j]为0,表示使数组的前0个元素都等于j所需的最小移动次数为0。
  3. 对于i>0,计算dp[i][j]:
    • 如果j>nums[i],则dp[i][j] = dp[i-1][j-1] + 1。
    • 如果j<nums[i],则dp[i][j] = dp[i-1][j+1] + 1。
    • 如果j==nums[i],则dp[i][j] = dp[i-1][j]。
  4. 找出所有dp[n][j]中的最小值,其中n是数组的长度。

时间复杂度:
暴力法的時間複雜度為O(n^3)。动态规划的时间复杂度为O(n^2)。

空间复杂度:
暴力法的空间复杂度为O(n)。动态规划的空间复杂度为O(n^2)。

优化:
我们可以使用一些优化技巧来进一步提高动态规划算法的效率。例如,我们可以利用对称性来减少状态的数量。具体来说,我们可以定义状态dp[i][j],表示使数组前i个元素都等于j或者j+1所需的最小移动次数。这样,状态的数量就从n^2减少到n*(n+1)/2。

代码:

def minMoves2(nums):
    """
    :type nums: List[int]
    :rtype: int
    """
    n = len(nums)
    nums.sort()
    median = nums[n // 2]
    moves = 0
    for num in nums:
        moves += abs(num - median)
    return moves

结论:
462.最少移动次数使数组元素相等II是一道经典的动态规划题。通过使用动态规划,我们可以将时间复杂度从O(n^3)降低到O(n^2),从而大大提高了解题效率。