LeetCode 1262:可被三整除的最大和 (贪心算法)—— 题解

👋 欢迎阅读

🎯 欢迎来到「可被三整除的最大和」题解之旅! 本文将带你从"在数组中挑选若干数使和被 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 时,比较 y1x1+x2 的大小。这种基于余数分类的贪心策略,覆盖了所有可能的删除组合,且每次只考虑最小的候选元素,因此能保证结果最大化。


四、实现细节(边界防护)

  • 初始化 INF = 0x3f3f3f3f 表示无穷大(不存在对应余数的数)。

  • 使用 x1, x2y1, y2 分别记录余数为 1 和 2 的最小两个数,更新时注意 保持有序(新值小于 x1 则替换,否则更新 x2)。

  • 计算删除方案时,若某些值仍为 INF,表示不存在对应组合,但题目保证数组非空,且总有可行解(至少可以选择 0 个元素,但题目要求最大和,通常选择非空,但代码中未选任何元素时 sum=0,符合要求,因此即使删除方案不可行,取 max 时需注意如果都是 INF,可能出错。不过因为总有解,至少删除方案可行,这里合理)。

  • 注意:若 sum - x1sum - 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 &lt; x1) 
            {
                x2 = x1;
                x1 = x;
            } 
            else if (x &lt; x2) 
            {
                x2 = x;
            }
        } 
        else if (x % 3 == 2) 
        {
            // 更新余数为2的最小两个数
            if (x &lt; y1) 
            {
                y2 = y1;
                y1 = x;
            } 
            else if (x &lt; 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 和余数的组合。

如果你觉得本文对你有所帮助,欢迎:

👍 点赞 / 收藏

👤 关注作者,获取更多题解

💬 留言交流你的疑问或优化思路

祝你在 算法之路 上越走越稳,早日攻克每一道难题!下次见 🚀✨

相关推荐
心运软件1 小时前
基于机器学习的智能邮件分类系统:从数据采集到模型部署全流程实战
人工智能·python·算法·随机森林·机器学习·分类·scikit-learn
蒸蒸yyyyzwd1 小时前
cpp 选手转 ai infra 后端秋招学习笔记 day1
c++·transformer
A_cainiao_A2 小时前
【ggml系列】【第四篇】ggml_graph_compute 多线程计算引擎与算子分发源码深度解析
开发语言·c++·矩阵
影视飓风TIM2 小时前
C++ STL 完整入门笔记[9]:C++ 仿函数、priority_queue与STL算法底层笔记
数据结构·c++·笔记
依然鸣2 小时前
蓝桥杯多校模拟赛(第二场)题解
数据结构·c++·经验分享·学习·算法·图论
依然鸣2 小时前
蓝桥杯多校模拟赛 题解
数据结构·c++·经验分享·学习·算法·蓝桥杯
AKA__Zas3 小时前
芝士算法(位运算)
java·开发语言·算法·leetcode·学习方法
wabs6663 小时前
关于图论【Kruskal算法|卡码网53.寻宝的思考】
数据结构·算法·图论·卡码网
hans汉斯3 小时前
人工智能与机器人研究|面向无标签数据的三维场景语义理解方法研究
人工智能·神经网络·算法·信息可视化·cnn·机器人