整数拆分 图解题解
这道题到底在问什么
- 输入
- n = 10
- 输出
- 36
先想最直接的笨办法
记住这条转移式——每一格都在套它,枚举第一段、剩下要么停要么续拆。(动画第 3 步)
最优解:为什么这么做
一句话答案:LeetCode 343 整数拆分求把 n 拆成至少两段的最大乘积。动态规划:dp[i] 枚举第一段 j,在 j×(i-j) 停拆和 j×dp[i-j] 续拆间取大,因剩段可拆可不拆,时间 O(n²)、空间 O(n)。
整数拆分这道题到底要最大化什么
给一个正整数 n,把它拆成至少两个正整数相加 n=a1+a2+…+ak(k≥2),让这些数的乘积 a1×a2×…×ak 最大,返回这个最大乘积。题面例子 n=10,最好的拆法是 3+3+4,乘积 3×3×4=36,就是答案。这里必须拆成两段以上,不能原样留一个 n 不动。
为什么枚举所有拆法会指数爆炸
最直接的想法是把所有拆法都列出来比乘积。可 n 能拆成两段、三段、更多段,每段还能取不同大小,方案随 n 指数级膨胀,稍大就列不完。而且很多拆法共用同一截尾巴——拆 10 时剩下的 8 怎么拆,和单独拆 8 是同一个子问题,被反复重算。把「拆 i 能得的最大乘积」算一次存下来复用,指数枚举就压成动态规划(把子问题算过的结果存下、后面直接取用)。
dp[i] 定成什么,dp[1] 为什么要设 1
定义 dp[i] 为「把整数 i 拆成至少两段能得到的最大乘积」,答案就是 dp[n]。下标 i 是被拆的数、格子里存它拆开后的最大积。开头 dp[1]=1 是个约定:1 没法再拆成两个正整数,本不该有值,但它会在别人「续拆」时被当乘数用,设成 1 表示「乘上这一段不改变积」。别把 dp[1] 设成 0,否则任何用到它的续拆方案都会被乘成 0,整条转移崩掉。
为什么每格要在拆到底和再拆一次之间取大
算 dp[i] 时枚举第一段取多大,记作 j,从 1 扫到 i-1。第一段定成 j 之后,剩下的 i-j 有两条路:一是就让它整段留着不再拆,这段贡献是 i-j 本身,得 j×(i-j);二是把 i-j 也拆到最优,贡献是 dp[i-j],得 j×dp[i-j]。两条取大、再对所有 j 取大,就是 dp[i]=max(j×(i-j), j×dp[i-j])。
为什么非得留「不拆」这一路?因为 dp[i-j] 是「至少拆两段」的积,有时反而更小。比如剩下 3,拆成 1+2 积才 2,还不如整个 3 留着。少了 j×(i-j) 这支,小段被强行拆开就会算亏。
拿 n=10 亲手把这张 dp 表填一遍
从小往大填。dp[1]=1 打底,dp[2] 只能 1+1 得 1。dp[3] 整段留着 1×2=2 胜过续拆。dp[4] 第一段取 2,2×2=4。dp[5] 第一段取 2,2×3=6。dp[6] 第一段取 3,3×3=9。到 dp[7] 续拆更划算,第一段 2 接续拆的 2×dp[5]=2×6=12。dp[8] 得 2×dp[6]=2×9=18,dp[9] 得 3×dp[6]=3×9=27,dp[10] 得 2×dp[8]=2×18=36。dp[10]=36,正是 3+3+4 那组,和题面对上。
复杂度是多少,n=2 和 n=3 两个边界怎么收
外层枚举被拆的数 i、内层枚举第一段 j,两层循环 O(n²);一维 dp 数组 O(n) 空间。两个小边界要想清:n=2 只能拆 1+1,dp[2]=1,别被「乘积该更大」的直觉带偏;n=3 最优是 1+2 得 2,不是硬凑更多段。还有内层 j 只能取到 i-1,得保证至少拆成两段,写成取到 i 就等于允许「原样不拆」,会把 dp[i] 顶成 i 而算错。
▶ 动画逐步走查(共 22 步)——想跟着动画一帧帧对照就展开
- 3记住这条转移式——每一格都在套它,枚举第一段、剩下要么停要么续拆。
- 4上行是下标 i(固定参照),下行 dp 待填。i=1 没法再拆,约定 dp[1]=1 当作「续拆」的基。
- 51 无法拆成两个正整数,把 dp[1]=1 作为基准——它只在被别人「续拆」时当乘数用。
- 6算 dp[2]:枚举第一段 j 后最划算的是 j=1 —— 第一段 1、剩下 1 不拆,1×1=1(在 1..1 里它最大)。
- 7把 1 填进 dp[2]。剩下那段不拆更值。
- 8算 dp[3]:枚举第一段 j 后最划算的是 j=1 —— 第一段 1、剩下 2 不拆,1×2=2(在 1..2 里它最大)。
- 9把 2 填进 dp[3]。剩下那段不拆更值。
- 10算 dp[4]:枚举第一段 j 后最划算的是 j=2 —— 第一段 2、剩下 2 不拆,2×2=4(在 1..3 里它最大)。
- 11把 4 填进 dp[4]。剩下那段不拆更值。
- 12算 dp[5]:枚举第一段 j 后最划算的是 j=2 —— 第一段 2、剩下 3 不拆,2×3=6(在 1..4 里它最大)。
- 13把 6 填进 dp[5]。剩下那段不拆更值。
- 14算 dp[6]:枚举第一段 j 后最划算的是 j=3 —— 第一段 3、剩下 3 不拆,3×3=9(在 1..5 里它最大)。
- 15把 9 填进 dp[6]。剩下那段不拆更值。
- 16算 dp[7]:枚举第一段 j 后最划算的是 j=2 —— 第一段 2、剩下 5 继续最优拆 dp[5]=6,2×6=12(在 1..6 里它最大)。
- 17把 12 填进 dp[7]。剩下那段继续拆更值。
- 18算 dp[8]:枚举第一段 j 后最划算的是 j=2 —— 第一段 2、剩下 6 继续最优拆 dp[6]=9,2×9=18(在 1..7 里它最大)。
- 19把 18 填进 dp[8]。剩下那段继续拆更值。
- 20算 dp[9]:枚举第一段 j 后最划算的是 j=3 —— 第一段 3、剩下 6 继续最优拆 dp[6]=9,3×9=27(在 1..8 里它最大)。
- 21把 27 填进 dp[9]。剩下那段继续拆更值。
- 22算 dp[10]:枚举第一段 j 后最划算的是 j=2 —— 第一段 2、剩下 8 继续最优拆 dp[8]=18,2×18=36(在 1..9 里它最大)。
- 23把 36 填进 dp[10]。剩下那段继续拆更值。
- 24最右 dp[10]=36 就是把 10 拆开能得到的最大乘积(对应 10=3+3+4,3×3×4=36)。
⚠️ 容易写错的地方
✗ 错:忘了拆完还能再拆
✓ 对:剩下 i-j 用 dp[i-j] 表示继续最优拆
j×dp[i-j] 这一支
✗ 错:dp[1] 设成 0
✓ 对:dp[1]=1 作续拆基
设 0 会把含 1 的续拆乘成 0
✗ 错:j 只取到 i
✓ 对:j∈1..i-1,必须至少拆成两段
至少两个正整数
完整代码(Python / C++ / Java)
Python
def integerBreak(n):
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]C++
int integerBreak(int n){
vector<int> dp(n + 1, 0);
dp[1] = 1;
for(int i = 2; i <= n; ++i)
for(int j = 1; j < i; ++j)
dp[i] = max({dp[i], j * (i - j), j * dp[i - j]});
return dp[n];
}Java
int integerBreak(int n){
int[] dp = new int[n + 1];
dp[1] = 1;
for(int i = 2; i <= n; i++)
for(int j = 1; j < i; j++)
dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
return dp[n];
}复杂度
时间
O(n²)
每个 i 枚举 j=1..i-1
空间
O(n)
一维 dp 数组
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 整数拆分 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么 dp[1] 要设成 1 而不是 0?+
dp[1] 只会在续拆那一支 j×dp[i-j] 里当乘数出现。1 本身拆不成两段、没有合法的拆分乘积,但续拆时它代表「剩下这一段就是 1」。设成 1,乘上去不改变积,符合实际;设成 0,所有落到剩 1 的续拆方案会被乘成 0,把正确答案压掉。
有没有不用 DP 的 O(1) 数学解?+
有。规律是尽量拆成 3:n 除 3 余 0 就全拆 3;余 1 时退一个 3、换成 2+2(因为 2×2=4 比 3×1=3 大);余 2 时在拆 3 的基础上多乘一个 2。这样能 O(1) 直接算出结果。本题用 DP 是为了讲清「拆到底还是留整」的决策结构,也更容易迁移到别的拆分问题。
只枚举第一段 j、没再拆 j,会不会漏掉「把第一段也拆开」的情况?+
不会漏。j 从 1 扫到 i-1,覆盖了第一段的所有取值;第一段之后的部分要不要继续拆,全交给 dp[i-j] 递归处理。任何一种拆法都能对应到「某个第一段 j + 剩下按 dp[i-j] 最优拆」,所以第一段自己不必再拆,所有方案都被这层枚举兜住了。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 整数拆分 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
把这道题真正拿下
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。