青训营学习收获6
(1)AI 刷题:数轴上的石子游戏(难度中)
问题描述
小S在数轴上放置了三枚石子,位置分别是 a, b, c。在每一回合中,小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),表示石子在数轴上的位置。
- 输出:一个整数,表示使石子连续所需的最小移动次数。
解题思路、思考过程
- 排序石子位置:为了便于计算间隔,首先将石子的位置进行排序,得到 (x), (y), (z)。
- 计算间隔:
- 计算第一个间隔:(gap1 = y - x)
- 计算第二个间隔:(gap2 = z - y)
- 判断最少移动次数:
- 如果两个间隔均为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,只需一次移动。
- 其他情况下,需要两次移动。
这样,通过简单的逻辑判断,我们可以快速得到使石子连续的最小移动次数。
执行结果
知识总结
积累知识点
-
排序的应用:
- 在解决涉及位置或顺序的问题时,排序是一个常用的技巧。通过排序,我们可以更方便地处理间隔和位置关系。
-
间隔计算:
- 间隔的计算是确定位置关系的基础。通过计算相邻元素之间的间隔,可以帮助我们判断状态和所需的操作。
-
条件判断的优化:
- 在编写条件判断时,优先考虑特殊情况(如间隔为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。 - 输出:一个字符串,表示两个二进制数的十进制和。
解题思路、思考过程
- 二进制转整数:使用 Python 的内置函数
int()将二进制字符串转换为整数。 - 整数求和:对转换后的整数进行加法运算。
- 整数转字符串:将求和结果转换为字符串,以便输出。
代码详解/完整解答
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()将整数结果转换为字符串。
执行结果
知识总结
积累知识点
-
二进制转换:
- 使用
int()函数进行二进制到十进制的转换,简化了处理大数的复杂度。
- 使用
-
字符串处理:
- 运用 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
问题关键
需要通过递归和回溯的方法探索每个指标的所有变换组合,并计算组合的乘积是否在给定区间内。
输入输出
-
输入:
n:指标的数量。f:一个二维数组,其中f[i]表示第i个指标的两种变换值。L:结果的下界。R:结果的上界。
-
输出:一个整数,表示符合条件的变换组合数量。
解题思路、思考过程
- 递归回溯:利用递归函数逐一尝试每个指标的变换选项。
- 初始条件:从第一个指标开始,初始乘积设为1。
- 递归终止条件:当所有指标都被考虑时,检查当前乘积是否在 [L,R][L,R] 内。
- 剪枝优化:如果当前乘积超过 RR,则提前终止该路径的计算。
- 组合计数:在递归过程中,累计符合条件的组合数量。
代码详解/完整解答
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,则不再递归。
- 组合计数:通过递归返回值累加,得到符合条件的组合数量。
执行结果
知识总结
积累知识点
-
递归与回溯:
- 递归是处理组合问题的有效方法,通过回溯可以探索所有可能的路径。
-
剪枝优化:
- 在递归过程中,及时剪掉不可能满足条件的路径,提升算法效率。
-
边界条件处理:
- 确保处理好递归的终止条件,以避免不必要的计算和错误。
个人理解
递归和回溯是解决组合问题的经典方法,结合条件判断和剪枝,可以大大提高算法效率。理解问题的本质,合理设计递归函数,并进行有效的剪枝,是解决此类问题的关键。
入门学习建议
- 多熟悉和练习递归、回溯等:这些是处理组合问题的基础。
- 思考优化剪枝策略:在递归中加入合理的剪枝条件,可以提高效率。
- 总结积累经验:在每次解决问题后,总结使用的技巧和方法,帮助更快解决类似问题。
(4)有关制定刷题学习计划的想法、建议
-
明确目标:先确定你刷题的目的,是为了面试、比赛,还是提升编程能力。不同的目标需要不同的策略。
-
评估基础:评估自己的当前水平,找出薄弱环节。可以通过做几道基础题来了解自己的长处和短板。
-
选择题目:根据目标和评估结果,选择合适的题目。可以从简单到难逐步挑战,也可以针对性地选择特定类型的题目(如动态规划、图论等)。
-
制定计划:制定合理的计划,每天或每周固定时间刷题。保持适当的强度,确保持续性,但也要避免过度疲劳。
-
定期回顾:定期回顾已做过的题目,尤其是那些曾经卡住的题目。复习有助于巩固知识,避免遗忘。
-
总结反思:每次刷题后,记录下解题思路和心得体会。总结常见的解题模式和技巧,逐渐形成自己的解题套路。
-
寻求反馈:参加讨论或向他人请教,获取不同的解题思路和建议。可以加入刷题小组或在线社区,互相交流学习。
-
调整策略:根据进展和反馈,及时调整刷题策略。适当增加难度或改变题目类型,以保持挑战性和新鲜感。
-
保持动力:给自己设定小目标和奖励机制,保持刷题的热情和动力。因为刷题是一个长期积累的过程。
-
最重要的还是坚持。