LeetCode 1458题"两个子序列的最大点积"是一个典型的动态规划问题。题目给定两个数组nums1和nums2,要求找到它们各自的一个非空子序列,使得这两个子序列的点积最大。子序列的定义是保持相对顺序但不要求连续的序列。
我第一次看到这个问题时,直觉告诉我这应该用动态规划来解决,因为:
定义dp[i][j]为nums1前i个元素和nums2前j个元素能形成的最大点积。这里i和j分别表示考虑nums1的前i个元素和nums2的前j个元素。
状态转移需要考虑四种情况:
因此状态转移方程为:
dp[i][j] = max(
nums1[i-1] * nums2[j-1], // 情况1
dp[i-1][j-1] + nums1[i-1] * nums2[j-1], // 情况2
dp[i-1][j], // 情况3
dp[i][j-1] // 情况4
)
dp[0][0] = 0,表示两个空子序列的点积为0。其他边界情况:
python复制def maxDotProduct(nums1, nums2):
m, n = len(nums1), len(nums2)
dp = [[-float('inf')] * (n+1) for _ in range(m+1)]
for i in range(1, m+1):
for j in range(1, n+1):
product = nums1[i-1] * nums2[j-1]
dp[i][j] = max(
product,
dp[i-1][j-1] + product,
dp[i-1][j],
dp[i][j-1]
)
return dp[m][n]
可以优化空间复杂度到O(n):
python复制def maxDotProduct(nums1, nums2):
m, n = len(nums1), len(nums2)
prev = [ -float('inf') ] * (n+1)
for i in range(1, m+1):
curr = [ -float('inf') ] * (n+1)
for j in range(1, n+1):
product = nums1[i-1] * nums2[j-1]
curr[j] = max(
product,
prev[j-1] + product,
prev[j],
curr[j-1]
)
prev = curr
return prev[n]
时间复杂度:O(mn),需要填充m×n的DP表
空间复杂度:基础实现O(mn),优化后O(n)
python复制# 示例1
nums1 = [2,1,-2,5]
nums2 = [3,0,-6]
# 输出: 18 (取子序列[2,-2]和[3,-6])
# 示例2
nums1 = [3,-2]
nums2 = [2,-6,7]
# 输出: 21 (取子序列[3]和[7])
这种类型的动态规划问题在以下场景有应用:
在解决这个问题时,我最初忽略了负数相乘可能得到更大正数的情况,导致几次提交失败。后来通过添加所有可能的状态转移情况解决了这个问题。这也提醒我,在动态规划问题中,考虑所有可能的选择非常重要。