👋 欢迎阅读

🎯 欢迎来到「可被三整除的最大和」题解之旅! 本文将带你从"在数组中挑选若干数使和被 3 整除且最大"这一组合优化问题出发,深入理解数学分类讨论 + 贪心删数 的巧妙思路,并掌握如何基于总和余数来微调,避免复杂的 DP 或枚举。
在开始之前,建议你先:
-
了解题目背景 :这是 LeetCode 1262 题,给定正整数数组
nums,要求选出任意个数 (可以不选),使得它们的和能被 3 整除 ,并求该和的最大值。本质上,我们不必枚举所有子集,而是先取所有数之和 ,然后根据其除以 3 的余数,删除最小的一批数来调整余数,使结果最大且满足条件。 -
明确学习目标 :掌握核心贪心分类策略 ------先求总和
sum,若sum % 3 == 0直接返回;若余数为 1,则要么删除一个最小的余数为 1 的数,要么删除两个最小的余数为 2 的数 (因为2+2 ≡ 1 mod 3),取删除后总和较大的方案;若余数为 2,则对应删除一个余数为 2 的数或两个余数为 1 的数。理解这种"用最小的损失换取余数归零 "的策略为什么是最优的(因为总和固定,删除越少、删除的数越小,剩余和越大),并熟练实现维护每种余数的最小两个值的代码。 -
准备好环境 :建议在本地 IDE 或 LeetCode 在线编辑器中打开代码,边看边运行,亲手验证示例(如
nums = [3,6,5,1,8]输出18)。
本文将从问题转化、余数分类讨论、贪心删数逻辑 到代码实现,层层递进。即使你对数论分类还不熟悉,我们也会从"总和已经最大,只需要微调余数"这一直觉出发,让你轻松抓住核心思想 ------总和减掉最少的数,使得余数变为 0,答案就是最大的合法和。现在,让我们一起通过最精简的删除,找出那个能被 3 整除的最大和吧! 🔢✨
一、题目
1262. 可被三整除的最大和 - 力扣(LeetCode)

二、做题思路
一、问题分析(前置分析)
给定一个正整数数组 nums,要求选取 任意个元素 (可以不选),使得这些元素的和 能被 3 整除,并返回该和的最大值。若无法选到非空子集满足条件,返回 0。
核心转化 :正难则反------先计算 所有元素的总和 sum ,然后根据 sum % 3 的余数,删除尽可能少的元素 ,使剩余总和能被 3 整除。删除元素的总和应 尽可能小,以保留最大和。
分类依据 :根据 sum % 3 的余数(0、1、2),需要删除的元素组合如下:
-
余数为 0:无需删除,直接返回
sum。 -
余数为 1:删除 一个余数为 1 的最小元素 ,或 两个余数为 2 的最小元素(因为 2+2 ≡ 1 mod 3)。
-
余数为 2:删除 一个余数为 2 的最小元素 ,或 两个余数为 1 的最小元素(因为 1+1 ≡ 2 mod 3)。
二、贪心策略(核心决策规则)
-
第一步 :遍历数组,累加总和
sum,同时记录 余数为 1 的最小两个数x1, x2和 余数为 2 的最小两个数y1, y2。 -
第二步 :根据
sum % 3分类:-
若
sum % 3 == 0:直接返回sum。 -
若
sum % 3 == 1:从sum中减去x1(删除一个余1的最小值)或减去y1 + y2(删除两个余2的最小值),取两者中使剩余总和更大的方案。 -
若
sum % 3 == 2:从sum中减去y1(删除一个余2的最小值)或减去x1 + x2(删除两个余1的最小值),取两者中使剩余总和更大的方案。
-
-
第三步 :返回调整后的
sum(此时必能被 3 整除)。
三、正确性说明(简单版本)
为了保证删除元素的总和最小 ,删除的必须是相应余数类别中 数值最小的元素。因为删除的元素越少、值越小,剩余总和就越大。
若余数为 1,有两种删除方式:删除一个余1的最小值(x1)或删除两个余2的最小值(y1+y2)。选择删除和较小的方案,即可得到最大剩余和。
同理,余数为 2 时,比较 y1 与 x1+x2 的大小。这种基于余数分类的贪心策略,覆盖了所有可能的删除组合,且每次只考虑最小的候选元素,因此能保证结果最大化。
四、实现细节(边界防护)
-
初始化
INF = 0x3f3f3f3f表示无穷大(不存在对应余数的数)。 -
使用
x1, x2和y1, y2分别记录余数为 1 和 2 的最小两个数,更新时注意 保持有序(新值小于 x1 则替换,否则更新 x2)。 -
计算删除方案时,若某些值仍为
INF,表示不存在对应组合,但题目保证数组非空,且总有可行解(至少可以选择 0 个元素,但题目要求最大和,通常选择非空,但代码中未选任何元素时 sum=0,符合要求,因此即使删除方案不可行,取 max 时需注意如果都是 INF,可能出错。不过因为总有解,至少删除方案可行,这里合理)。 -
注意:若
sum - x1或sum - y1 - y2可能为负,但题目中所有数为正,且总能找到可行方案,故结果非负。 -
时间复杂度 O(n),空间 O(1)。
五、返回值(目标映射)
返回调整后的 sum,即 能被 3 整除的最大元素和 。若无可行非空子集(如数组只有一个数且不能被 3 整除),则返回 0(对应 sum=0 或删除所有元素的情况)。
四、代码
cpp
class Solution
{
public:
int maxSumDivThree(vector<int>& nums)
{
// 算法思路:先计算所有数的总和 sum。
// 最终和必须能被3整除,因此我们需要调整 sum 的余数。
// 若 sum % 3 == 0,直接返回 sum。
// 若 sum % 3 == 1,则需要删除一个余数为1的数,或删除两个余数为2的数(因为2+2=4≡1 mod 3),
// 为了最大化结果,删除的数之和应尽可能小,所以分别记录余数为1的最小值x1、余数为2的两个最小值y1,y2。
// 若 sum % 3 == 2,则需要删除一个余数为2的数,或删除两个余数为1的数(1+1=2 mod 3),
// 同理使用最小值的组合。
const int INF = 0x3f3f3f3f; // 足够大的数,表示不存在对应余数的数
int sum = 0; // 所有元素的总和
int x1 = INF, x2 = INF; // 余数为1的数中的最小值和次小值
int y1 = INF, y2 = INF; // 余数为2的数中的最小值和次小值
// 1. 遍历数组,累加总和,并记录每种余数的最小两个数
for (auto x : nums)
{
sum += x;
if (x % 3 == 1)
{
// 更新余数为1的最小两个数
if (x < x1)
{
x2 = x1;
x1 = x;
}
else if (x < x2)
{
x2 = x;
}
}
else if (x % 3 == 2)
{
// 更新余数为2的最小两个数
if (x < y1)
{
y2 = y1;
y1 = x;
}
else if (x < y2)
{
y2 = x;
}
}
}
// 2. 根据总和的余数进行分类讨论
if (sum % 3 == 1)
{
// 需要将余数从1调整为0,有两种删除方案:
// - 删除一个余数为1的最小数 x1
// - 删除两个余数为2的最小数 y1 + y2(因为 2+2≡1 mod 3,相当于消去余数1)
// 取删除后剩余总和最大的方案,即删除和最小的方案
sum = max(sum - x1, sum - y1 - y2);
}
else if (sum % 3 == 2)
{
// 需要将余数从2调整为0,有两种删除方案:
// - 删除一个余数为2的最小数 y1
// - 删除两个余数为1的最小数 x1 + x2(因为 1+1≡2 mod 3)
sum = max(sum - x1 - x2, sum - y1);
}
// 若 sum % 3 == 0,直接返回原总和
// 3. 返回调整后的最大和(保证能被3整除)
return sum;
}
};
五、流程图
六、正确性说明(详细版)
步骤 1:符号与问题建模
+----------------------------------------------------+
| 输入:整数数组 nums |
| 目标:选取若干元素,使它们的和**最大**且**能被3整除**|
| 若无法选出,返回 0 |
+----------------------------------------------------+
|
v
+----------------------------------------------------+
| 贪心策略(正难则反): |
| ① 先计算所有元素的总和 sum。 |
| ② 若 sum % 3 == 0,**直接返回 sum**。 |
| ③ 若 sum % 3 == 1,则需要**删除**一个余数为1的数 |
| 或两个余数为2的数(因为 2+2 ≡ 1 mod 3), |
| 选择删除和最小的方案。 |
| ④ 若 sum % 3 == 2,则需要**删除**一个余数为2的数 |
| 或两个余数为1的数(因为 1+1 ≡ 2 mod 3), |
| 选择删除和最小的方案。 |
| 贪心实质:**从全集中删除最少的必要元素**, |
| 使总和余数变为0,且删除的代价最小, |
| 从而保留最大总和。 |
+----------------------------------------------------+
-
将数组元素按模3的余数分为三类:余数0、余数1、余数2。
-
设
sum为所有元素的总和。 -
核心观察 :从总和中删除 若干个元素,等价于从原数组中排除这些元素。为了使结果最大,应删除尽可能少且值尽可能小的元素。
步骤 2:关键性质 ------ 分类讨论与贪心选择的最优性(反证法)
+----------------------------------------------------+
| 性质 1:若 sum % 3 == 0,则所有元素之和本身 |
| 就是最大可被3整除的和,**无需删除任何元素**。 |
| 反证:若删除某些元素,总和会变小,不可能更优。 |
+----------------------------------------------------+
|
v
+----------------------------------------------------+
| 性质 2:若 sum % 3 == 1,则为了将余数变为0, |
| 必须删除的元素组合只能是以下两种之一: |
| ① 删除一个余数为1的数(贡献余数1); |
| ② 删除两个余数为2的数(2+2 ≡ 1 mod 3)。 |
| 为了最大化剩余和,应选择删除和最小的组合。 |
| 反证:若删除组合的总和不是最小的,则存在更小的 |
| 删除组合使剩余和更大,矛盾。 |
+----------------------------------------------------+
|
v
+----------------------------------------------------+
| 性质 3:若 sum % 3 == 2,则必须删除的元素组合 |
| 只能是以下两种之一: |
| ① 删除一个余数为2的数(贡献余数2); |
| ② 删除两个余数为1的数(1+1 ≡ 2 mod 3)。 |
| 同理,选择删除和最小的组合。 |
| 反证:若删除组合不是最小,则存在更优方案,矛盾。 |
+----------------------------------------------------+
详细论证:
-
性质1(余数为0) :若
sum % 3 == 0,则总和已经满足条件。任何删除操作都会使总和减少,不可能得到比sum更大的可被3整除的和 ,因此直接返回sum是最优的。 -
性质2(余数为1) :设当前总和
S ≡ 1 (mod 3)。要得到可被3整除的和,必须通过删除元素来调整余数。删除一个元素对总和余数的影响等于该元素的模3余数的相反数(即减去该余数)。因此:-
删除一个余数为1的元素:
1 - 1 ≡ 0 (mod 3),有效。 -
删除两个余数为2的元素:
1 - 2 - 2 ≡ 1 - 4 ≡ -3 ≡ 0 (mod 3),有效。 -
删除四个余数为2的元素也可行,但代价更大(删除更多数),不可能优于两个最小值的组合。
-
删除一个余数为1加一个余数为0的元素:余数不变,无效。
因此只有上述两种方案。为了使剩余和最大,应选择删除和最小的方案。若选择不是最小的删除组合,则存在更小的删除和使剩余和更大,与最优性矛盾。
-
-
性质3(余数为2) :若
S ≡ 2 (mod 3),同理可得:-
删除一个余数为2的元素:
2 - 2 ≡ 0 (mod 3),有效。 -
删除两个余数为1的元素:
2 - 1 - 1 ≡ 0 (mod 3),有效。同样选择删除和最小的方案。任何其他删除组合要么无效,要么代价更大。
-
-
补充说明 :为了求删除和最小,我们只需要每种余数的最小值和次小值(记录两个最小的数即可),因为最多只需要删除两个数。
步骤 3:归纳证明 ------ 扫描过程正确维护最小值和次小值
+----------------------------------------------------+
| 初始:x1=INF, x2=INF, y1=INF, y2=INF。 |
| 归纳假设:遍历完前 i 个元素后,x1 和 x2 是余数 |
| 为1的数中的最小和次小值,y1 和 y2 是余数为2的 |
| 数中的最小和次小值。 |
+----------------------------------------------------+
|
v
+----------------------------------------------------+
| 处理第 i+1 个元素 x: |
| 若 x%3==1: |
| - 若 x < x1,则 x2=x1, x1=x; |
| - 否则若 x < x2,则 x2=x。 |
| 若 x%3==2:同理更新 y1, y2。 |
| 余数为0的数无需记录(因为不会用于删除)。 |
| 更新后,归纳假设保持。 |
+----------------------------------------------------+
|
v
+----------------------------------------------------+
| 遍历结束,得到所有需要的最大值和次小值。 |
| 根据步骤2的分类讨论,选择最优删除方案, |
| 返回剩余和,即为最大可被3整除的和。 |
+----------------------------------------------------+
详细论证:
-
归纳基础 :遍历前,没有处理任何元素,
x1=x2=y1=y2=INF(表示不存在),正确。 -
归纳步骤 :假设已处理前
i个元素,x1, x2, y1, y2分别正确记录了对应余数的最小和次小值。现在处理第i+1个元素x:-
若
x % 3 == 1,则它属于余数1类。若x < x1,则新的最小值是x,原最小值变为次小值;否则若x < x2,则它成为新的次小值。其他情况(x ≥ x2)不需要更新,因为它不会影响最小两个值的选取。 -
若
x % 3 == 2,同理更新y1, y2。 -
若
x % 3 == 0,删除它不会改变余数,且删除它会减少总和,在任何方案中都不会被删除,因此无需记录。
-
-
更新后,
x1, x2, y1, y2仍然正确表示已处理元素中对应余数的最小和次小值,归纳假设成立。 -
终止:遍历完所有元素后,根据步骤2的分类讨论,用记录的最小值组合计算最优删除方案,剩余和即为最大可被3整除的和。
🎯 闭幕
🎉 恭喜你完成了「可被三整除的最大和」问题的学习!
为了巩固知识并进一步拓展,建议你:
🚀 动手实践
在 LeetCode 上提交代码,尝试不同的测试用例。
💡 深入思考
-
本题采用 贪心 + 分类讨论 :先计算总和,再根据总和模 3 的余数,删除最小数量的特定余数元素 ,使总和被 3 整除。为什么只需要考虑删除 1 个余数为 1 的数或 2 个余数为 2 的数(当余数为 1 时)? 能否删除更多个数来获得更大的和?为什么?
-
代码中分别记录了余数为 1 和 2 的 最小值和次小值 。为什么需要次小值? 如果只需要最小值,在需要删除两个数时会出现什么问题?
-
如果
sum % 3 == 1,有两种删除方案:删除一个余数为 1 的最小数,或删除两个余数为 2 的最小数。为什么删除两个余数为 2 的数等价于删除一个余数为 1 的数? (提示:模运算性质)
📚 延伸挑战
- 将题目改为 可被 k 整除的最大和(k 为任意正整数),你会如何设计算法?提示:需要记录每种余数的最小几个数,具体数量取决于 k 和余数的组合。
如果你觉得本文对你有所帮助,欢迎:
👍 点赞 / 收藏
👤 关注作者,获取更多题解
💬 留言交流你的疑问或优化思路
祝你在 算法之路 上越走越稳,早日攻克每一道难题!下次见 🚀✨