在问 · 算法题解

343. 整数拆分

题目描述

输入一个正整数 n。把它写成至少两个正整数的和,即 n = a1 + a2 + ... + ak,其中 k >= 2。

在所有拆法中,让这些数的乘积尽量大。返回能得到的最大乘积。

示例 1
输入:n = 2
输出:1

解释:只能拆成 1 + 1,乘积为 1。

示例 2
输入:n = 10
输出:36

解释:拆成 3 + 3 + 4,乘积 3 × 3 × 4 = 36 最大。

数据范围

  • 2 <= n <= 58

解题思路

dp[i] 表示把 i 拆成至少两个数的最大乘积。枚举拆出的第一个数 j,剩下的 i-j 有两种处理:不再拆(乘积 j*(i-j)),或继续拆(乘积 j*dp[i-j])。取所有 j 与两种方式中的最大值。

为什么正确

计算 dp[i] 时,dp[1..i-1] 都已是最终值。

复杂度分析

时间 O(n^2),空间 O(n)。

易错点

不能只用 j*dp[i-j]:dp[2]=1、dp[3]=2 比 2、3 本身还小,必须同时考虑“剩下部分不拆”的 j*(i-j)。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def integerBreak(self, n: int) -> int:
        dp = [0] * (n + 1)
        dp[1] = 1
        for i in range(2, n + 1):
            for j in range(1, i):
                dp[i] = max(dp[i], j * (i - j), j * dp[i - j])
        return dp[n]

载入编辑器,自己跑一遍 →

其他解法

动态规划逐一枚举第一段的长度,通用但是 O(n²);数学做法直接得出最优拆分的结构,O(1)。

解法 2:数学(尽量拆出 3) 时间 O(1),空间 O(1)

可以证明:拆出的因子只用 2 和 3,并且 3 越多越好。2 和 3 必须拆成 1 加一个数,分别返回 1 和 2。否则看 n 除以 3 的余数:余 0 就是 3 的 n/3 次方;余 1 时把一个 3+1 改成 2+2,得到 3 的 (n/3-1) 次方乘 4;余 2 就是 3 的 n/3 次方乘 2。

常数时间,代码很短。但正确性依赖数学证明(大于 4 的因子必然可以再拆以增大乘积,而 3×3 > 2×2×2),面试时不容易当场论证,也不容易推广到其他限制条件。

class Solution:
    def integerBreak(self, n: int) -> int:
        if n <= 3:
            return n - 1
        q, r = divmod(n, 3)
        if r == 0:
            return 3 ** q
        if r == 1:
            return 3 ** (q - 1) * 4
        return 3 ** q * 2

动态规划 专题的其他题目

查看 动态规划 全部题目 →