青训营X豆包MarsCode 技术训练营第十课 | 豆包MarsCode AI 刷题

115 阅读15分钟

问题描述

给定两个长度为 n 的整数数组 a 和 b,你可以对第一个数组 a 中的每个元素执行以下操作至多一次:

  • 将元素变为其相反数(即将 x 变为 -x)

你的目标是通过合理的操作,使得两个数组之间的"绝对值距离"最小。

两个数组的"绝对值距离"定义为:对于数组中每个对应位置的元素,计算它们绝对值之差的绝对值,然后将所有差值相加。

用数学公式表示为:distance=∑∣abs(ai)−abs(bi)∣distance=∑∣abs(ai​)−abs(bi​)∣,其中 0 ≤ i < n。

请返回可以得到的最小距离。

  • 思路

    • 本题的核心思路是通过分析每个对应位置元素的两种可能情况(保持原数或者变为其相反数)来计算出最小的 “绝对值距离”。对于数组 a 和 b 中每一对对应位置的元素 a[i] 和 b[i],考虑将 a[i] 变为其相反数或者保持不变这两种操作,分别计算出 |a[i] - b[i]|(保持 a[i] 不变时两数绝对值之差的绝对值)和 |a[i] + b[i]|(将 a[i] 变为其相反数后两数绝对值之差的绝对值),然后取这两个差值中的较小值,因为这个较小值就是在当前位置通过合理选择操作(变或者不变)所能得到的最小差值。最后将所有位置的最小差值累加起来,得到的总和就是两个数组之间的最小 “绝对值距离”。
  • 图解(以简单示例示意,假设 n = 3a = [2, -3, 4]b = [-1, 3, -2]

    1. 对于位置 i = 0

      • 计算 |a[0] - b[0]|,即 |2 - (-1)| = 3
      • 计算 |a[0] + b[0]|,即 |2 + (-1)| = 1
      • 取较小值 1,这就是在该位置能得到的最小差值。
    2. 对于位置 i = 1

      • 计算 |a[1] - b[1]|,即 |-3 - 3| = 6
      • 计算 |a[1] + b[1]|,即 |-3 + 3| = 0
      • 取较小值 0,作为该位置的最小差值。
    3. 对于位置 i = 2

      • 计算 |a[2] - b[2]|,即 |4 - (-2)| = 6
      • 计算 |a[2] + b[2]|,即 |4 + (-2)| = 2
      • 取较小值 2,作为该位置的最小差值。
    4. 计算最小距离

      • 将各个位置的最小差值累加起来,即 1 + 0 + 2 = 3,所以这两个数组的最小 “绝对值距离” 为 3
  • 代码详解

public class Main {
    public static int solution(int n, int[] a, int[] b) {
        long totalDistance = 0;
        for (int i = 0; i < n; i++) {
            // Calculate |a[i] - b[i]| and |a[i] + b[i]|
            long diff1 = Math.abs((long)a[i] - (long)b[i]);
            long diff2 = Math.abs((long)a[i] + (long)b[i]);
            // Choose the smaller one and add to the total distance
            totalDistance += Math.min(diff1, diff2);
        }
        return (int)totalDistance;
    }

    public static void main(String[] args) {
        // Test cases
        System.out.println(solution(3, new int[]{-1, 2, 3}, new int[]{-3, 2, -1}) == 4); // Expected: 4
        System.out.println(solution(4, new int[]{-1, 2, 3, 4}, new int[]{1, -2, 5, -4}) == 2); // Expected: 2
        System.out.println(solution(2, new int[]{100, -50}, new int[]{-50, 100}) == 100); // Expected: 100
    }
}
  • 初始化与循环部分

    • 首先定义一个 long 类型的变量 totalDistance 并初始化为 0,用于累加计算得到的最小距离值。这里使用 long 类型是为了防止在计算过程中出现整数溢出的情况,因为数组元素的取值范围较大(-10^9 ≤ a[i], b[i] ≤ 10^9),差值累加可能超出 int 的表示范围。然后通过 for 循环遍历数组,索引 i 从 0 到 n - 1n 是数组长度),对每一对对应位置的元素进行操作。
  • 计算差值并选择较小值部分

    • 在每次循环中,首先计算两种情况下的差值绝对值。通过 (long)a[i] 和 (long)b[i] 将 int 类型的数组元素转换为 long 类型(同样是为了避免溢出),然后计算 diff1 = Math.abs((long)a[i] - (long)b[i]),即保持 a[i] 不变时与 b[i] 的绝对值之差的绝对值;以及 diff2 = Math.abs((long)a[i] + (long)b[i]),即把 a[i] 变为其相反数后与 b[i] 的绝对值之差的绝对值。
    • 接着使用 Math.min(diff1, diff2) 选择这两个差值中的较小值,将其累加到 totalDistance 中,通过 totalDistance += Math.min(diff1, diff2) 实现。
  • 返回结果部分

    • 循环结束后,通过 (int)totalDistance 将 long 类型的累加结果转换回 int 类型(因为题目要求返回 int 类型的结果),然后返回这个值,它就是两个数组之间的最小 “绝对值距离”。
  • 主函数 main 部分

    • 在 main 方法中,主要是对 solution 方法进行简单的测试验证,通过传入不同的测试样例对应的参数(数组长度 n,以及两个数组 a 和 b),并将 solution 方法的返回结果与预期结果进行比较(通过 == 判断是否相等),然后将比较得到的布尔值结果输出到控制台,以此来检查 solution 方法在不同输入情况下是否能正确工作。

知识总结

  • 新知识点梳理

    • 对数组元素进行合理变换求最优值的思想:本题通过考虑对数组 a 中元素变为其相反数这一操作,然后在两种可能情况(变与不变)下寻找最优的选择来使得最终的 “绝对值距离” 最小,这种通过尝试不同操作并比较结果来求最优值的思路在很多算法问题中都很常见,尤其是涉及到状态变化和最值求解的场景。
    • 使用 Math 类的方法进行数学运算和比较大小:熟练运用了 Math.abs() 方法来计算绝对值,以及 Math.min() 方法来选择两个值中的较小值,了解到 Math 类提供了很多方便的数学运算相关的静态方法,可以在处理数值计算问题时直接调用,简化代码实现并提高代码的可读性。
  • 理解

    • 对于通过元素变换求最优值的思想,就像是在面对多种选择时,通过逐一分析每种选择带来的结果,然后挑选出最好的那个选项,在本题中就是分析每个元素变或者不变产生的不同距离差值,从而找到整体最小的距离。而 Math 类的相关方法则是为我们提供了快捷准确的数学计算工具,不用自己去编写复杂的绝对值计算或者比较大小的逻辑,让代码更加简洁高效,专注于解决问题的核心逻辑。
  • 学习建议(针对入门同学)

    • 在学习这种通过元素变换求最优值的思路时,要多从简单的例子入手,自己手动去分析不同操作下的结果变化,比如可以改变题目中的数组元素值,手动计算各种情况下的距离,感受如何通过比较选择出最优的操作方式。然后做一些类似的基础题目,尝试总结出这类问题的一般解题步骤,例如先确定有哪些可变的操作,再分析每种操作对结果的影响,最后通过比较得出最优解,逐步培养这种优化选择的思维方式。
    • 对于 Math 类的方法,要先熟悉常用的几个方法(如 absminmaxsqrt 等)的功能和参数要求,可以编写一些简单的代码示例,分别调用这些方法传入不同的参数,观察输出结果,了解它们的具体用法。在实际解题过程中,有意识地去运用这些方法来简化代码,当遇到涉及数学运算和比较大小的情况时,优先考虑是否可以直接使用 Math 类提供的方法来实现,通过不断实践来熟练掌握它们的使用技巧。

学习计划

  • 制定刷题计划

    • 基础巩固阶段

      • 目标设定:熟练掌握数组的基本操作、简单的数值计算以及通过有限操作求最优值的基础思路,能够独立完成类似本题这种难度较低、逻辑相对直接的题目,并且对代码的语法规范和逻辑结构有较好的把握。
      • 刷题范围:以数组操作、简单数学运算结合的基础算法题目为主,可以在各大刷题平台(如 LeetCode、牛客网等)上筛选难度标记为简单的题目,或者在 MarsCode AI 刷题功能中查找对应知识点的基础题目集,例如涉及数组遍历、简单数值变换求最值等类型的题目。
      • 时间安排:每天安排 1 - 2 小时用于刷题,每次刷 2 - 3 道题。在刷题过程中,认真分析题目要求,梳理解题思路,自己动手编写代码实现,注意代码的规范性和可读性,写完后通过简单的测试用例(可以参考本题主函数中的测试方式)进行验证,确保代码能正确运行,同时记录下解题过程中遇到的问题和疑惑点,及时查阅资料解决。
      • 总结归纳:每完成 5 - 10 道题后,对涉及的知识点进行总结回顾,比如对比不同题目中数组操作的异同点,求最值思路的变化等,整理出自己的知识点笔记,加深对基础知识点的理解和记忆,并且可以对相似题目进行分类整理,便于后续复习和回顾。
    • 能力提升阶段

      • 目标设定:掌握中等难度的算法题目,能够灵活运用通过元素变换求最优值的思路解决更复杂的情况,比如涉及多个操作条件、多种数据结构结合以及更复杂的数值计算等,提高解题效率和代码质量,学会分析不同题目之间的关联和差异,优化解题思路。
      • 刷题范围:拓展到包含多种元素变换规则求最值、数组与其他数据结构(如哈希表、栈等)结合应用、复杂的数学运算与逻辑判断相结合的题目。可以在刷题平台上选择中等难度的题目板块,或者利用 MarsCode AI 刷题的智能推荐功能,根据已掌握的知识点推荐相应的中等难度题目。
      • 时间安排:每周抽出 3 - 4 天,每天安排 2 - 3 小时刷题,每次完成 3 - 5 道题。在刷题过程中,注重思考题目与之前做过的基础题目的联系和区别,尝试多种解题思路,对于做错的题目要详细分析错误原因,记录在错题本上,并及时复习相关知识点进行查漏补缺,同时可以尝试对题目进行变形,自己拓展出类似的题目进行练习,加深对知识点的理解和运用能力。
      • 总结归纳:针对每类题型(如多种元素变换求最值题型、数据结构结合题型等)进行总结,梳理出这类题型的通用解题模板和注意事项,同时对这段时间内新学习的知识点和解题技巧进行整理,形成自己的知识体系框架,便于复习和回顾,并且可以通过绘制思维导图等方式将知识点之间的关联更加清晰地展现出来,帮助自己更好地理解和记忆。
    • 强化突破阶段

      • 目标设定:攻克高难度题目,深入理解在复杂场景下求最优值的深层次原理和优化策略,能够应对各种变形题目以及将相关思路拓展应用到其他未接触过的领域,提升在限时条件下解决复杂问题的能力,为应对竞赛或者面试中的难题做好准备。
      • 刷题范围:挑战高难度的算法竞赛真题、知名企业面试中的算法难题以及具有创新性和综合性的题目,例如那些将多种元素变换规则与动态规划、图论等复杂知识点融合的题目。可以参加线上的算法竞赛模拟赛、刷题打卡活动等,或者在 MarsCode AI 刷题中专门挑战高难度题目集。
      • 时间安排:每周安排 2 - 3 天,每天 3 - 4 小时用于高难度刷题。每次完成 1 - 2 道题,因为高难度题目往往需要花费大量时间去分析思考和尝试不同解法,所以要注重解题过程中的思路拓展和方法优化,多参考优秀的解题代码和思路解析,与其他刷题者交流讨论,拓宽自己的思维视野,同时对自己知识体系中的薄弱环节进行重点突破,通过专项练习和深入学习不断完善自己的知识储备和解题能力。
  • 利用错题进行针对性学习

    • 错题整理:建立电子错题本或者纸质错题本,将做错的题目按照知识点模块(如元素变换求最值错误、数学运算错误等)、错误类型(思路错误、代码实现错误、细节遗漏等)进行分类整理,详细记录题目内容、自己的错误解法、正确解法以及错误原因分析,并且标注出涉及的知识点和需要重点复习的地方,例如如果是因为对通过元素变换求最优值的思路理解不清导致错误,就重点标注这一知识点需要重新学习巩固。
    • 定期复习与分析:每周安排固定时间(如周末 1 - 2 小时)复习错题,重新做一遍错题,看是否能够正确解答,如果再次做错,仔细分析是之前的错误原因没有彻底解决,还是又出现了新的问题,对于仍然存在的知识漏洞或者解题思维误区进行重点标记,然后针对这些问题进行专项学习,可以重新查阅教材、观看相关知识点的讲解视频或者请教老师、同学等,确保彻底掌握相关知识点,避免下次再犯同样的错误。
    • 举一反三与拓展:在掌握错题的正确解法后,思考这道错题涉及的知识点还能如何在其他题目场景中运用,尝试对题目进行变形,自己出题并解答,例如改变本题中数组元素的取值范围、增加元素变换的操作种类等,通过这种方式加深对知识点的理解深度和运用的灵活性,真正做到举一反三,提高自己的解题能力。

工具运用

  • 与教材结合:在使用 AI 刷题时,可以搭配一本系统全面的数据结构和算法教材,比如《算法导论》《数据结构与算法分析:Java 语言描述》等。在刷题前,先通过教材学习相关知识点的理论内容,例如数组的定义、操作特点,数学运算在算法中的常见应用以及求最值的基本算法思想等,构建扎实的知识基础。然后在刷题过程中,如果遇到对某个知识点理解模糊或者不清楚的地方,及时返回教材中对应的章节进行深入复习,加深理解,让刷题有更坚实的理论支撑。同时,教材中的例题和练习题可以作为补充练习,进一步巩固所学知识点,帮助自己更好地将理论知识应用到实际解题中。
  • 与在线课程互动:报名参加一些优质的在线算法课程,例如慕课网、网易云课堂上的相关课程。在课程学习过程中,老师会讲解知识点以及通过示例题目演示解题思路,此时可以结合 AI 刷题功能,在课后去查找相同知识点或者相似题型的题目进行练习巩固。同时,如果在刷题时遇到难题或者有疑问,可以在课程的答疑社区或者交流群里向老师和其他同学请教,获取更多的思路和帮助,也可以将自己通过 AI 刷题总结出的独特解题方法分享出来,互相学习交流,提高学习效果。另外,在线课程中往往会布置一些课后作业和项目实践,通过认真完成这些任务,可以将所学知识应用到实际场景中,加深对知识点的理解和掌握程度。
  • 与开源项目学习协同:在 GitHub 等平台上搜索一些与数据结构和算法相关的开源项目,这些项目往往会在实际代码中运用到各种算法和数据结构知识。在学习过程中,可以一边通过 AI 刷题提升算法解题能力,一边去阅读开源项目中的代码,分析其中是如何运用所学的算法和数据结构的,比如在一个数据处理项目中如何通过元素变换来优化数据存储或者计算效率等。通过这种方式,将理论知识与实际项目中的应用相结合,更好地理解算法和数据结构的实用价值,进一步提高自己的编程实践能力。同时,还可以参与开源项目的开发,通过实际贡献代码,锻炼自己的编程技能,并且从其他开发者的代码中学习到更多优秀的编程习惯和算法应用技巧。