返回

LeetCode 321:巧用动态规划算法,拼接出最大数

前端

动态规划的精髓:化繁为简,逐步求解

动态规划是一种解决优化问题的常用算法,它的核心思想是将一个复杂的问题分解成一系列相互关联的子问题,然后从最简单的子问题开始,逐步求解,最终得到最优解。在LeetCode 321题中,我们可以将拼接数字的过程看作是一个动态规划问题,具体步骤如下:

  1. 明确子问题和状态

    子问题:对于数组A和数组B的长度分别为i和j,从这两个数组中选择k个元素拼接成一个新的数,使得该数尽可能大,这个最大数是多少?

    状态:dp[i][j][k]表示从数组A的前i个元素和数组B的前j个元素中,选择k个元素拼接成一个新的数,使得该数尽可能大的最大值。

  2. 确定初始条件

    当i=0或j=0时,dp[i][j][k]=0,因为没有任何元素可供选择。

  3. 状态转移方程

    对于给定的i, j和k,有两种情况:

    • 情况1: 从数组A中选择一个元素。此时,dp[i][j][k]=max(dp[i-1][j][k], 10*dp[i-1][j][k-1]+A[i])。
    • 情况2: 从数组B中选择一个元素。此时,dp[i][j][k]=max(dp[i][j-1][k], 10*dp[i][j-1][k-1]+B[j])。
  4. 计算最优解

    最终,我们可以得到dp[m][n][k],其中m和n分别为数组A和数组B的长度,k为要拼接的元素数量。这个值就是从数组A和数组B中选择k个元素拼接成一个新的数,使得该数尽可能大的最大值。

算法实现与实例解析

为了更好地理解动态规划算法在LeetCode 321题中的应用,我们通过代码示例来进一步解析:

def maxNumber(nums1, nums2, k):
  m, n = len(nums1), len(nums2)
  dp = [[[0] * (k + 1) for _ in range(n + 1)] for _ in range(m + 1)]

  for i in range(1, m + 1):
    for j in range(1, n + 1):
      for l in range(1, min(i + j, k) + 1):
        dp[i][j][l] = max(dp[i-1][j][l], dp[i][j-1][l], 10 * dp[i-1][j][l-1] + nums1[i-1], 10 * dp[i][j-1][l-1] + nums2[j-1])

  res = []
  i, j, l = m, n, k
  while l > 0:
    if dp[i][j][l] == 10 * dp[i-1][j][l-1] + nums1[i-1]:
      res.append(nums1[i-1])
      i -= 1
    elif dp[i][j][l] == 10 * dp[i][j-1][l-1] + nums2[j-1]:
      res.append(nums2[j-1])
      j -= 1
    l -= 1

  return ''.join(map(str, res[::-1]))

对于输入数组nums1=[3, 4, 6, 5]和nums2=[9, 1, 2, 5, 8, 3],以及k=5,我们使用上述算法可以得到最大拼接数为98653。

结语

动态规划算法是一种强大的工具,它可以帮助我们解决许多复杂的优化问题。在LeetCode 321题中,我们通过将拼接数字的过程看作是一个动态规划问题,并逐步求解,最终得到了最大拼接数。这种方法不仅清晰易懂,而且具有很强的通用性,可以应用于各种类型的优化问题。