青训营学习收获6 | 豆包MarsCode AI 刷题

114 阅读12分钟

青训营学习收获6

(1)AI 刷题:数轴上的石子游戏(难度中)

问题描述

小S在数轴上放置了三枚石子,位置分别是 abc。在每一回合中,小S可以选择从最左边或最右边的石子,并将其移动到它们中间的某个空闲位置。目标是通过最少的移动次数,使得三枚石子的位置连续,即不能再进行任何移动。你需要帮助小S找到游戏结束所需的最小移动次数。

测试样例

样例1:

输入:a = 1, b = 2, c = 5
输出:1

样例2:

输入:a = 4, b = 3, c = 2
输出:0

样例3:

输入:a = 3, b = 5, c = 1
输出:1

问题关键

每一回合,可以选择从最左边或最右边的石子,并将其移动到它们中间的某个空闲位置。目标是通过最少的移动次数,使得三枚石子的位置连续,即不能再进行任何移动。

输入输出

  • 输入:三个整数 (a), (b), (c),表示石子在数轴上的位置。
  • 输出:一个整数,表示使石子连续所需的最小移动次数。

解题思路、思考过程

  1. 排序石子位置:为了便于计算间隔,首先将石子的位置进行排序,得到 (x), (y), (z)。
  2. 计算间隔
    • 计算第一个间隔:(gap1 = y - x)
    • 计算第二个间隔:(gap2 = z - y)
  3. 判断最少移动次数
    • 如果两个间隔均为1(即 (gap1 == 1) 且 (gap2 == 1)),说明石子已经连续,不需要移动。
    • 如果任一间隔为2(即 (gap1 == 2) 或 (gap2 == 2)),只需一次移动即可使石子连续。
    • 其他情况下,需要两次移动才能使石子连续。

代码详解/完整解答

def solution(a: int, b: int, c: int) -> int:
    # 将石子位置排序
    x, y, z = sorted([a, b, c])
    
    # 计算间隔
    gap1 = y - x
    gap2 = z - y
    
    # 判断最少移动次数
    if gap1 == 1 and gap2 == 1:
        return 0
    elif gap1 <= 2 or gap2 <= 2:
        return 1
    else:
        return 2

if __name__ == '__main__':
    print(solution(a=1, b=2, c=5) == 1)  # 输出应为 True
    print(solution(a=4, b=3, c=2) == 0)  # 输出应为 True
    print(solution(a=3, b=5, c=1) == 1)  # 输出应为 True

代码解释

  • 排序:使用 sorted() 函数对石子的位置进行排序,以便于计算间隔。

  • 计算间隔:通过简单的减法计算两个相邻石子之间的间隔。

  • 判断条件

    • 如果两个间隔都是1,说明石子已经连续。
    • 如果任一间隔是2,只需一次移动。
    • 其他情况下,需要两次移动。

这样,通过简单的逻辑判断,我们可以快速得到使石子连续的最小移动次数。

执行结果

image.png

知识总结

积累知识点

  1. 排序的应用

    • 在解决涉及位置或顺序的问题时,排序是一个常用的技巧。通过排序,我们可以更方便地处理间隔和位置关系。
  2. 间隔计算

    • 间隔的计算是确定位置关系的基础。通过计算相邻元素之间的间隔,可以帮助我们判断状态和所需的操作。
  3. 条件判断的优化

    • 在编写条件判断时,优先考虑特殊情况(如间隔为1或2)的处理,可以简化逻辑和减少错误。

个人理解

在解决问题时,理解问题的本质和寻找简化问题的方法是关键。例如,通过排序将问题简化为处理有序数列的问题,可以大大降低复杂度。同时,条件判断的优化可以帮助快速找到解决方案。

入门学习建议

  • 多练习排序和间隔计算:这些是许多算法问题的基础,熟练掌握它们可以帮助你更好地理解和解决问题。
  • 简化问题思考:先尝试将复杂问题简化为更简单的子问题,逐步解决。
  • 优化条件判断:在编写代码时,尽量优化判断条件,减少不必要的复杂性。
  • 多做总结、积累:在解决每个问题后,总结所学到的知识和技巧,帮助自己在未来更快地解决类似问题。

(2)AI 刷题:二进制之和(难度难)

问题描述

小U和小R喜欢探索二进制数字的奥秘。他们想找到一个方法,将两个二进制字符串相加并以十进制的形式呈现。这个过程需要注意的是,他们的二进制串可能非常长,所以常规的方法可能无法处理大数。小U和小R希望你帮助他们设计一个算法,该算法能在保证时间复杂度不超过O(n^2)的前提下,返回两个二进制字符串的十进制求和结果。

测试样例

样例1:

输入:binary1 = "101"binary2 = "110"
输出:"11"

样例2:

输入:binary1 = "111111"binary2 = "10100"
输出:"83"

样例3:

输入:binary1 = "111010101001001011"binary2 = "100010101001"
输出:"242420"

样例4:

输入:binary1 = "111010101001011"binary2 = "10010101001"
输出:"31220"

样例5:

输入:binary1 = "11"binary2 = "1"
输出:"4"

问题关键

将两个二进制字符串转化为整数后求和,然后将结果转换为十进制字符串输出。

输入输出

  • 输入:两个二进制字符串 binary1 和 binary2
  • 输出:一个字符串,表示两个二进制数的十进制和。

解题思路、思考过程

  1. 二进制转整数:使用 Python 的内置函数 int() 将二进制字符串转换为整数。
  2. 整数求和:对转换后的整数进行加法运算。
  3. 整数转字符串:将求和结果转换为字符串,以便输出。

代码详解/完整解答

def solution(binary1, binary2):
    # 将二进制字符串转换为整数
    num1 = int(binary1, 2)
    num2 = int(binary2, 2)
    
    # 转换后的整数进行加法运算
    total = num1 + num2
    
    # 求和结果转换为字符串输出
    return str(total)

if __name__ == "__main__":
    print(solution("101", "110") == "11")
    print(solution("111111", "10100") == "83")
    print(solution("111010101001001011", "100010101001") == "242420")
    print(solution("111010101001011", "10010101001") == "31220")
    print(solution("11", "1") == "4")

代码解释

  • 二进制转整数int(binary, 2) 将二进制字符串转换为十进制整数。
  • 整数求和:直接对两个整数进行加法运算。
  • 结果转换str() 将整数结果转换为字符串。

执行结果

image.png

知识总结

积累知识点

  1. 二进制转换

    • 使用 int() 函数进行二进制到十进制的转换,简化了处理大数的复杂度。
  2. 字符串处理

    • 运用 Python 的字符串处理功能,可以轻松实现各种格式转换。

个人理解

通过利用 Python 的内置功能,可以高效地处理二进制字符串的加法问题。理解如何使用这些内置函数对于解决类似问题非常重要。

入门学习建议

  • 熟悉一些 Python 内置函数:掌握 int() 和 str() 等函数的用法,在一些情况可以帮助快速解决问题。
  • 熟悉进制的转换:通过练习和积累,熟悉不同进制之间的转换。
  • 理解问题本质和思考简化:在解决问题时,可以关注如何简化步骤和减少复杂度。

(3)AI 刷题:融合目标计算问题(难度中)

问题描述

小F正在开发一个推荐系统,该系统使用多个模型来预估不同的目标指标(如点击率和观看时长)。为了有效地融合这些指标,系统对每个目标提供了两种不同的变换方式,每种方式会产生一个变换值。小F的任务是为每个目标选择其中一种变换方式,然后将所有选中的变换值相乘,得到最终的融合结果。

然而,不同的选择组合可能会导致最终结果值过大或过小。因此,小F希望计算出在给定的合理区间 [L,R][L,R] 内,有多少种不同的选择组合可以使最终的融合结果落在这个区间内。

输入说明:

  • n:目标指标的数量
  • f:一个二维数组,其中 f[i] 表示第 i 个目标的两种变换值
  • L:期望结果的下界
  • R:期望结果的上界

取值范围:

  • 1 ≤ n ≤ 20
  • 1 ≤ f[i][j] ≤ 10^9
  • 1 ≤ L ≤ R ≤ 10^9

输出说明:

输出一个整数,表示使最终融合结果落在区间 [L,R][L,R] 内的选择组合数量。

测试样例

样例1:

输入:n = 2, f = [[1, 2], [3, 4]], L = 1, R = 6
输出:3

样例2:

输入:n = 2, f = [[1, 2], [3, 4]], L = 4, R = 6
输出:2

样例3:

输入:n = 3, f = [[1, 2], [3, 5], [2, 4]], L = 10, R = 50
输出:7

image.png

问题关键

需要通过递归和回溯的方法探索每个指标的所有变换组合,并计算组合的乘积是否在给定区间内。

输入输出

  • 输入

    • n:指标的数量。
    • f:一个二维数组,其中 f[i] 表示第 i 个指标的两种变换值。
    • L:结果的下界。
    • R:结果的上界。
  • 输出:一个整数,表示符合条件的变换组合数量。

解题思路、思考过程

  1. 递归回溯:利用递归函数逐一尝试每个指标的变换选项。
  2. 初始条件:从第一个指标开始,初始乘积设为1。
  3. 递归终止条件:当所有指标都被考虑时,检查当前乘积是否在 [L,R][L,R] 内。
  4. 剪枝优化:如果当前乘积超过 RR,则提前终止该路径的计算。
  5. 组合计数:在递归过程中,累计符合条件的组合数量。

代码详解/完整解答

def solution(n: int, f: list[list[int]], L: int, R: int) -> int:
    def count_combinations(index: int, current_product: int) -> int:
        # 基本情况:如果所有指标都已考虑
        if index == n:
            return 1 if L <= current_product <= R else 0
        
        # 递归情况:尝试当前指标的两种变换
        count = 0
        for transformation in f[index]:
            new_product = current_product * transformation
            # 只有当新乘积小于等于上界时才继续
            if new_product <= R:
                count += count_combinations(index + 1, new_product)
        
        return count
    
    # 从第一个指标开始递归,初始乘积为1
    return count_combinations(0, 1)

# 例子测试用例
print(solution(2, [[1, 2], [3, 4]], 1, 6))  # 输出:3
print(solution(2, [[1, 2], [3, 4]], 4, 6))  # 输出:2
print(solution(3, [[1, 2], [3, 5], [2, 4]], 10, 50))  # 输出:7

代码解释

  • 递归函数count_combinations 用于计算从当前指标开始的有效组合数量。
  • 初始调用:从第一个指标开始,初始乘积为1。
  • 剪枝:如果当前乘积已经超过 RR,则不再递归。
  • 组合计数:通过递归返回值累加,得到符合条件的组合数量。

执行结果

image.png

知识总结

积累知识点

  1. 递归与回溯

    • 递归是处理组合问题的有效方法,通过回溯可以探索所有可能的路径。
  2. 剪枝优化

    • 在递归过程中,及时剪掉不可能满足条件的路径,提升算法效率。
  3. 边界条件处理

    • 确保处理好递归的终止条件,以避免不必要的计算和错误。

个人理解

递归和回溯是解决组合问题的经典方法,结合条件判断和剪枝,可以大大提高算法效率。理解问题的本质,合理设计递归函数,并进行有效的剪枝,是解决此类问题的关键。

入门学习建议

  • 多熟悉和练习递归、回溯等:这些是处理组合问题的基础。
  • 思考优化剪枝策略:在递归中加入合理的剪枝条件,可以提高效率。
  • 总结积累经验:在每次解决问题后,总结使用的技巧和方法,帮助更快解决类似问题。

(4)有关制定刷题学习计划的想法、建议

  1. 明确目标:先确定你刷题的目的,是为了面试、比赛,还是提升编程能力。不同的目标需要不同的策略。

  2. 评估基础:评估自己的当前水平,找出薄弱环节。可以通过做几道基础题来了解自己的长处和短板。

  3. 选择题目:根据目标和评估结果,选择合适的题目。可以从简单到难逐步挑战,也可以针对性地选择特定类型的题目(如动态规划、图论等)。

  4. 制定计划:制定合理的计划,每天或每周固定时间刷题。保持适当的强度,确保持续性,但也要避免过度疲劳。

  5. 定期回顾:定期回顾已做过的题目,尤其是那些曾经卡住的题目。复习有助于巩固知识,避免遗忘。

  6. 总结反思:每次刷题后,记录下解题思路和心得体会。总结常见的解题模式和技巧,逐渐形成自己的解题套路。

  7. 寻求反馈:参加讨论或向他人请教,获取不同的解题思路和建议。可以加入刷题小组或在线社区,互相交流学习。

  8. 调整策略:根据进展和反馈,及时调整刷题策略。适当增加难度或改变题目类型,以保持挑战性和新鲜感。

  9. 保持动力:给自己设定小目标和奖励机制,保持刷题的热情和动力。因为刷题是一个长期积累的过程。

  10. 最重要的还是坚持。