👋 欢迎阅读

🎯 欢迎来到「最优除法」题解之旅! 本文将带你从"通过添加括号改变除法优先级"这一算术问题出发,深入理解贪心算法 + 数学推导 的精妙之处,并掌握如何利用连除性质快速构造出使结果最大的表达式。
在开始之前,建议你先:
-
了解题目背景 :这是 LeetCode 553 题,给定一个正整数数组
nums,相邻整数按顺序进行浮点除法(如[2,3,4]表示2/3/4),你可以在任意位置添加括号改变运算顺序,目标是使最终结果最大 ,并以字符串形式返回对应表达式(不含冗余括号)。本质上,这是一个关于除法优先级的最优化问题。 -
明确学习目标 :掌握贪心推导的结论 ------对于
nums[0] / nums[1] / ... / nums[n-1],最大结果表达式为nums[0] / (nums[1] / nums[2] / ... / nums[n-1]),即把除了第一个数以外的所有数用括号括起来并连续相除 。理解为什么这样构造能最大化(因为除法中分母越小结果越大,而将后面的数相除会使分母最小化),并熟练处理边界情况 (n=1或n=2时无需括号)。
一、题目

二、做题思路
1. 问题分析(前置分析)
给定一个正整数数组 nums,要求通过添加括号改变浮点除法的运算顺序,使得整个表达式的值 最大 。
基础形式 是 nums[0] / nums[1] / nums[2] / ...,括号可以改变除法的结合性。
核心观察 :由于所有数均为 正整数 ,且数组长度至少为 2,将第一个数作为分子,其余所有数作为分母 ,并将分母部分整体括起来,能使结果最大化。
2. 贪心策略(核心决策规则)
-
特殊情况 :若
n == 1,直接返回该数。 -
特殊情况 :若
n == 2,返回nums[0] / nums[1],无需括号。 -
一般情况(n >= 3):
-
构造表达式:
nums[0] / (nums[1] / nums[2] / ... / nums[n-1]) -
这等价于
nums[0] * nums[2] * ... * nums[n-1] / nums[1],即 分子为第一个数乘以除第二个数以外的所有数,分母仅为第二个数。 -
因为所有数 >= 2,这种形式使得分母尽可能小(仅一个数),从而结果最大。
-
3. 正确性说明(简单版本)
设数组为 a, b, c, d, ...(所有数 ≥ 2)。
原始表达式 a / b / c / ... 默认从左到右,相当于 a / (b * c * ...)。
在分母中加入乘法会扩大分母,从而减小整体值 。为了最大化结果,我们应尽可能让分母变小 ,即让尽量少的数留在分母 。
通过添加括号,我们可以将 a 以外的所有数"压缩"到分子上,具体做法是 a / (b / c / d / ...),而 (b / c / d / ...) 等价于 b / (c * d * ...),但由于括号内的除法会变为乘法,最终结果化为 a * c * d * ... / b,这时分母仅剩 b,是可能的最简形式。
这种形式是唯一的全局最大值 ,因为任何其他加括号方式都会让至少一个数(除 b 外)留在分母,或让 b 移到分子,导致分母变大或分子变小,结果不会更大。
4. 实现细节(边界防护)
-
处理
n == 1和n == 2的特殊情况。 -
对于
n >= 3,构造字符串 :先添加nums[0] + "/(",然后循环添加nums[1]到nums[n-2]并用'/'连接,最后添加nums[n-1] + ")"。。
5. 返回值(目标映射)
返回构造好的最优表达式字符串,即 满足最大值的表达式。
四、代码
cpp
class Solution
{
public:
string optimalDivision(vector<int>& nums)
{
int n = nums.size();
// 1. 特殊情况:只有一个数时,直接返回该数(无除法)
if (n == 1)
{
return to_string(nums[0]);
}
// 2. 特殊情况:只有两个数时,直接相除,无需括号
if (n == 2)
{
return to_string(nums[0]) + '/' + to_string(nums[1]);
}
// 3. 一般情况:n >= 3
// 数学结论:为了使除法表达式结果最大,应该将 nums[0] 作为分子,
// 其余所有数作为分母,并且将分母部分用括号括起来,
// 即 nums[0] / (nums[1] / nums[2] / ... / nums[n-1])
// 这样等价于 nums[0] * nums[2] * ... * nums[n-1] / nums[1],
// 是能得到的最大值(因为所有数都为正整数)。
string ret;
// 构造:nums[0] + '/' + '('
ret += to_string(nums[0]) + '/' + '(';
// 中间部分:nums[1] / nums[2] / ... / nums[n-2](最后一项单独处理)
for (int i = 1; i < n - 1; i++)
{
ret += to_string(nums[i]) + '/';
}
// 最后一项:nums[n-1] + ')'
ret += to_string(nums[n - 1]) + ')';
// 4. 返回构造好的最优表达式字符串
return ret;
}
};
五、流程图
六、正确性说明(详细版)
步骤 1:符号与问题建模
+----------------------------------------------------+
| 输入:正整数数组 nums = [a₁, a₂, ..., aₙ] |
| 操作:在相邻数之间插入除号,可加任意括号改变优先级|
| 目标:使整个表达式的值最大,返回对应字符串 |
+----------------------------------------------------+
|
v
+----------------------------------------------------+
| 贪心策略:当 n ≥ 3 时,构造表达式 |
| a₁ / (a₂ / a₃ / ... / aₙ) |
| 即:a₁ 作为分子,其余所有数放在一个括号内连除。 |
| 该表达式等价于 a₁ * a₃ * a₄ * ... * aₙ / a₂。 |
| 贪心实质:通过括号将 a₂ 单独放在分母最底层, |
| 而 a₃..aₙ 变成乘数,最大化分子/分母比值。 |
+----------------------------------------------------+
-
设数组元素为 a₁, a₂, ..., aₙ(图二中用 a,b,c,d,e,f 表示)。
-
由于所有数均为正整数(且原题无限制,但图二提到 ≥2,这使结论更强),除法运算等价于乘以倒数,因此加括号的本质是决定哪些数乘到分子、哪些乘到分母。
-
贪心结论 :最优表达式为 a₁ / (a₂ / a₃ / ... / aₙ),即把第一个数作为分子,剩余所有数作为分母并整体括起来。
步骤 2:关键性质 ------ 最优表达式结构(反证法与不等式)
+----------------------------------------------------+
| 性质 1:任意加括号的表达式都可以化为 |
| a₁ * X / Y 的形式,其中 X 是 a₃..aₙ 中某些数的 |
| 乘积,Y 是 a₂..aₙ 中某些数的乘积,且 a₂ 必然在 Y |
| 中(因为 a₁ 是第一个数,除号紧跟在 a₁ 后)。 |
| 反证:若 a₂ 不在分母,则 a₂ 变为乘数,会增大 |
| 分子,但 a₂ 右侧的数会相应调整,总效果是 |
| 分母变小,但无法超过将 a₂ 单独放分母的极端情况。 |
+----------------------------------------------------+
|
v
+----------------------------------------------------+
| 性质 2(核心不等式):对于任意 i ≥ 3, |
| 将 a_i 放在分母中会除以 a_i,而放在分子中会乘以 |
| a_i。由于 a_i ≥ 2(图二条件),乘比除大, |
| 因此为了最大化,应让尽可能多的数(除 a₂ 外) |
| 进入分子。但 a₂ 必须留在分母(因为紧跟 a₁ 的除号 |
| 必然将 a₂ 置于分母),所以最佳是让 a₃..aₙ 全在 |
| 分子,即表达式为 a₁ / (a₂ / a₃ / ... / aₙ)。 |
| 不等式示意:若将某个 a_i (i≥3) 放到分母,则值 |
| 变为原来的 1/a_i² 倍(因为原来乘 a_i,现在除 |
| a_i),显然减小。 |
+----------------------------------------------------+
详细论证:
-
性质1(结构分解) :任何加括号的除法表达式,由于第一个数是
a₁,且第一个运算符是除号,所以a₁必然作为分子。其后的所有数通过括号和除法组织,最终可以看作a₁乘以某些数的乘积,再除以某些数的乘积。a₂必然出现在分母中 ,因为a₁ / a₂是最先形成的除法关系,无论括号如何,a₂不能变为乘数(除非有括号将其与后续数组合,但a₁除以的是一个整体,该整体包含a₂且a₂在分母位置)。 -
性质2(最优化选择) :对于任意
i ≥ 3,将a_i放在分子比放在分母更优 。若当前a_i在分母,将其移到分子(通过调整括号),表达式值会乘以a_i^2(因为原来除以a_i,现在乘以a_i)。由于a_i ≥ 2,a_i^2 > 1,值严格增大。因此,最优解必须让所有a₃..aₙ都在分子。唯一必须留在分母的是a₂,故最优形式为a₁ * a₃ * a₄ * ... * aₙ / a₂,即a₁ / (a₂ / a₃ / ... / aₙ)。
步骤 3:归纳证明 ------ 贪心构造即为全局最优
+----------------------------------------------------+
| 初始:n 个数,已确定 a₁ 为分子。 |
| 归纳假设:对于后缀 a₂..aₙ,最优分母构造是 |
| 让 a₂ 单独留在分母,其余数通过嵌套括号转化为乘数。 |
+----------------------------------------------------+
|
v
+----------------------------------------------------+
| 处理 a₂:它必须留在分母(因为紧跟 a₁ 的除号)。 |
| 处理 a₃..aₙ:由性质2,每个都应进入分子。 |
| 构造括号:a₂ / a₃ / ... / aₙ 整体作为分母, |
| 因为连除等价于 a₂ / (a₃ * ... * aₙ),即 a₂ 在 |
| 分母,a₃..aₙ 实际上变为乘数(因为除以一个除式 |
| 等于乘以该除式倒数的乘积)。 |
| 最终表达式 a₁ / (a₂ / a₃ / ... / aₙ) 达到最大。 |
+----------------------------------------------------+
|
v
+----------------------------------------------------+
| 结论:该贪心策略得到最大表达式值,且字符串为 |
| a₁/(a₂/a₃/.../aₙ)(去掉冗余括号后返回)。 |
+----------------------------------------------------+
- 终止 :当处理完所有数,得到表达式即为最大值。若
n=1或2,直接返回无需括号。
🎯 闭幕

🎉 恭喜你完成了「最优除法」问题的学习!
为了巩固知识并进一步拓展,建议你:
🚀 动手实践
在 LeetCode 上提交代码,尝试不同的测试用例。
💡 深入思考
-
本题采用 贪心策略 :对于长度大于 2 的数组,最优表达式为
nums[0] / (nums[1] / nums[2] / ... / nums[n-1])。为什么这样能让结果最大? 你能从除法的数学性质(例如除以一个分数等于乘以它的倒数)来解释吗? -
代码中分别处理了
n == 1和n == 2的情况。如果长度为 1 或 2 时也套用通用括号形式,会得到什么结果? 为什么需要单独处理? -
当
n >= 3时,括号从第二个数开始括到最后一个数。为什么不在第一个数后面直接加括号? 例如(nums[0]/nums[1])/nums[2]与nums[0]/(nums[1]/nums[2])哪种更大?用例子验证一下。
📚 延伸挑战
-
将题目改为 求表达式的最小值,你应该如何调整括号位置?只需要改变哪里的策略?
-
如果要求 返回最大值对应的数值(而不是表达式),代码需要做哪些修改?
如果你觉得本文对你有所帮助,欢迎:
👍 点赞 / 收藏
👤 关注作者,获取更多题解
💬 留言交流你的疑问或优化思路
祝你在 算法之路 上越走越稳,早日攻克每一道难题!下次见 🚀✨