题目解析:最小移动次数使组相等 | 豆包MarsCode AI刷题

98 阅读5分钟

题目描述

小C有两个长度为 N 的数组 A 和 B。他可以进行以下两种操作,来将数组 A 转换为数组 B:

  1. 反转数组 A,即使数组 A 的元素顺序完全颠倒。
  2. 在 [1, N] 范围内选择一个整数 i,然后可以对 A[i] 添加或减去任意值。

你的任务是帮助小C找到使数组 A 等于数组 B 所需的最小操作次数。

示例

  • 样例1

    • 输入:N = 3, A = [1, 2, 5], B = [4, 2, 1]
    • 输出:2
    • 解释:第一步反转数组 A,得到新数组 A = [5, 2, 1]。第二步从位置 1 减去 1,得到新数组 A = [4, 2, 1]。
  • 样例2

    • 输入:N = 4, A = [7, 8, 6, 2], B = [6, 2, 8, 7]
    • 输出:3
    • 解释:第一步反转数组 A,得到新数组 A = [2, 6, 8, 7]。第二步从位置 0 加上 4,得到新数组 A = [6, 6, 8, 7]。第三步从位置 1 加上 2,得到新数组 A = [6, 2, 8, 7]。
  • 样例3

    • 输入:N = 2, A = [3, 9], B = [9, 3]
    • 输出:1
    • 解释:直接反转数组 A 即可。

思路

  1. 直接比较:检查数组 A 是否已经等于数组 B。如果是,返回 0。
  2. 反转比较:检查将数组 A 反转后是否等于数组 B。如果是,返回 1。
  3. 逐个修改:计算 A 和 B 中对应位置不同的元素个数 diff_count
  4. 反转后再逐个修改:计算 A 反转后与 B 中对应位置不同的元素个数 diff_count_reversed,并考虑最小操作次数。

代码详解

python

def solution(N: int, A: list, B: list) -> int:
    # 直接比较
    if A == B:
        return 0
    
    # 反转比较
    A_reversed = A[::-1]
    if A_reversed == B:
        return 1
    
    # 逐个修改
    diff_count = sum(1 for i in range(N) if A[i] != B[i])
    
    # 反转后再逐个修改
    diff_count_reversed = sum(1 for i in range(N) if A_reversed[i] != B[i])
    
    # 返回最小操作次数
    return min(diff_count, 1 + diff_count_reversed)

if __name__ == '__main__':
    print(solution(N=3, A=[1, 2, 5], B=[4, 2, 1]) == 2)
    print(solution(N=4, A=[7, 8, 6, 2], B=[6, 2, 8, 7]) == 3)
    print(solution(N=2, A=[3, 9], B=[9, 3]) == 1)

知识总结

新知识点

  1. 列表切片A[::-1] 用于反转列表。
  2. 生成器表达式sum(1 for i in range(N) if A[i] != B[i]) 用于计算不同元素的个数。
  3. 最小操作次数:通过 min(diff_count, 1 + diff_count_reversed) 计算最小操作次数。

关键知识点详解

1. 列表切片

列表切片是 Python 中一种非常强大的功能,用于从列表中提取子列表。切片的基本语法是 list[start:stop:step],其中:

  • start 是切片的起始索引(包含)。
  • stop 是切片的结束索引(不包含)。
  • step 是步长,即每隔多少个元素取一个。
示例
A = [1, 2, 3, 4, 5]
print(A[1:4])  # 输出 [2, 3, 4]
print(A[:3])   # 输出 [1, 2, 3]
print(A[2:])   # 输出 [3, 4, 5]
print(A[::2])  # 输出 [1, 3, 5]
print(A[::-1]) # 输出 [5, 4, 3, 2, 1],反转列表

在本题中,我们使用 A[::-1] 来反转列表 A,这相当于创建了一个新的列表,其中元素顺序与原列表相反。

2. 生成器表达式

生成器表达式是一种简洁的方式来创建生成器对象。生成器对象可以在需要时生成值,而不是一次性生成所有值,因此在处理大数据时非常高效。

语法

生成器表达式的语法类似于列表推导式,但使用圆括号 () 而不是方括号 []

示例
numbers = [1, 2, 3, 4, 5]
squares = (x**2 for x in numbers)
print(list(squares))  # 输出 [1, 4, 9, 16, 25]

# 生成器表达式可以用于 sum 函数
sum_of_squares = sum(x**2 for x in numbers)
print(sum_of_squares)  # 输出 55

在本题中,我们使用生成器表达式 sum(1 for i in range(N) if A[i] != B[i]) 来计算 AB 中不同元素的个数。这比使用列表推导式更高效,因为生成器表达式不会一次性生成所有值,而是按需生成。

3. 最小操作次数

在解决这道题目时,我们需要找到将数组 A 转换为数组 B 的最小操作次数。我们考虑了以下几种操作:

  • 直接比较:如果 A 已经等于 B,则不需要任何操作。
  • 反转比较:如果将 A 反转后等于 B,则只需要一次操作。
  • 逐个修改:计算 A 和 B 中不同元素的个数 diff_count,这需要 diff_count 次操作。
  • 反转后再逐个修改:计算 A 反转后与 B 中不同元素的个数 diff_count_reversed,这需要 1 + diff_count_reversed 次操作。

方法选择

  1. 直接比较:这是最简单的一步,检查数组是否已经相等。
  2. 反转比较:反转数组后检查是否相等,这是一种常见的优化手段。
  3. 逐个修改:计算不同元素的个数,这是最基本的解决方案。
  4. 反转后再逐个修改:结合反转和逐个修改,找到最小操作次数。

问题及解决方法

  1. 问题:在某些情况下,代码可能没有正确处理所有可能的操作组合。

    • 解决方法:仔细检查每种操作的可能性,特别是反转后再逐个修改的情况。
  2. 问题:代码效率低下。

    • 解决方法:使用生成器表达式和列表切片等高效操作,减少不必要的计算。
  3. 问题:测试用例覆盖不全。

    • 解决方法:编写更多的测试用例,涵盖各种边界情况和特殊情况。

总结

通过这道题目,我学到了如何处理数组转换问题,掌握了列表切片和生成器表达式的使用方法。在解决问题时,逐步思考和优化代码是非常重要的。希望这些经验和建议对其他入门同学有所帮助。