观察数字拆分的模式,我们可以发现以下事实:
根据上面的事实,我们可以写出以下算法:
以下是基于上述方法的Python实现:
def integerBreak(n: int) -> int:
if n == 2:
return 1
if n == 3:
return 2
if n == 4:
return 4
# n > 4
a, b = divmod(n, 3)
if b == 0:
return 3**a
elif b == 1:
return 3**(a-1) * 4
else: # b == 2
return 3**a * 2
# 测试
print(integerBreak(2)) # 输出:1
print(integerBreak(10)) # 输出:36
这种方法的时间复杂度是 O(1),因为我们只是做了一些数学计算。
对于整数拆分问题,动态规划的一个直观思路是使用一个数组 dp,其中 dp[i] 表示数字 i 拆分后的最大乘积。
我们的目标是填充这个数组,以便 dp[n] 给出答案。
递推关系的建立需要考虑如何拆分整数 i。对于任何小于 i 的正整数 j,我们可以将 i 拆分为 j 和 i-j。这两个数字可以进一步拆分或保持原样。因此,乘积的最大值是 max(j, dp[j]) * max(i-j, dp[i-j])。
要计算 dp[i],我们考虑所有可能的 j(从1到i/2),并采取上述乘积的最大值。
基于上述逻辑,我们可以为问题建立动态规划解决方案。
def integerBreak(n: int) -> int:
# dp[i] 表示数字 i 拆分后的最大乘积
dp = [0] * (n+1)
dp[1] = 1
for i in range(2, n+1):
for j in range(1, i//2 + 1):
dp[i] = max(dp[i], max(j, dp[j]) * max(i-j, dp[i-j]))
return dp[n]
# 测试
print(integerBreak(2)) # 输出:1
print(integerBreak(10)) # 输出:36
这种动态规划方法的时间复杂度是 O(n^2),因为我们使用了两层循环来填充 dp 数组。
优化动态规划的方法涉及减少不必要的计算。对于整数拆分问题,我们注意到当我们选择拆分整数 i
为 j
和 i-j
时,由于问题是对称的,我们实际上只需要考虑从 1
到 i//2
的整数 j
。因此,我们可以减少一半的计算。
考虑到这一点,我们仍然需要确定如何拆分整数以获得最大的乘积。动态规划的核心仍然是使用 dp
数组,其中 dp[i]
表示整数 i
被拆分后的最大乘积。
考虑到我们的数学方法,整数3和2是最关键的因素。我们可以进一步优化我们的动态规划解决方案,使其更接近数学解决方案,但仍然使用动态规划的框架。
这是优化后的解决方案:
def integerBreak(n: int) -> int:
if n == 2:
return 1
if n == 3:
return 2
# dp[i] 表示数字 i 拆分后的最大乘积
dp = [0] * (n+1)
# 基本情况
dp[1], dp[2], dp[3] = 1, 2, 3
for i in range(4, n+1):
# 因为问题对称,我们只考虑从1到i//2的整数j
for j in range(1, i//2 + 1):
dp[i] = max(dp[i], dp[j] * dp[i-j])
return dp[n]
# 测试
print(integerBreak(2)) # 输出:1
print(integerBreak(10)) # 输出:36
尽管外观上与先前的动态规划解决方案相似,但这个解决方案的性能更好,因为它减少了不必要的计算,并且更加接近数学解决方案的思路。
这个问题可以使用动态规划来解决,基于以下观察:
当我们尝试构建一个二叉搜索树时,我们可以选择一个数字作为根。如果我们选择数字 i 作为根,那么所有小于 i 的数字必须位于它的左子树中,而所有大于 i 的数字则必须位于它的右子树中。
因此,对于一个给定的根 i,数量是:左子树的数量乘以右子树的数量。
我们可以使用动态规划来计算所有可能数量的总和。定义数组 G
,其中 G[n]
表示长度为 n
的序列的不同二叉搜索树的数量。
给定序列 1 … n,我们从序列中选择一个数字 i,将该数作为根,将 1 … (i-1) 序列作为左子树,将 (i+1) … n 序列作为右子树。因此,我们可以得出以下公式:
[ G(n) = G(0) \times G(n-1) + G(1) \times G(n-2) + … + G(n-1) \times G(0) ]
具体解决方案如下:
def numTrees(n: int) -> int:
G = [0] * (n + 1)
G[0], G[1] = 1, 1
for i in range(2, n + 1):
for j in range(1, i + 1):
G[i] += G[j - 1] * G[i - j]
return G[n]
# 测试
print(numTrees(3)) # 输出:5
print(numTrees(1)) # 输出:1
这个方法的时间复杂度是 ( O(n^2) )。