青训营X豆包MarsCode 小Q的奇偶操作数组 | 豆包MarsCode AI 刷题

50 阅读4分钟

学习笔记

问题解析

小Q有一个长度为 n 的数组,可以进行 k 次操作。每次操作可以对数组中的任意一个数字执行以下规则: 如果选择的数字 x 是奇数,则将 x 乘以 2,即 x = x * 2。 如果选择的数字 x 是偶数,则将 x 乘以 2 再加 1,即 x = x * 2 + 1。 我们的目标是通过 k 次操作,使得数组的元素之和最小。可以在每次操作中选择数组中的任意一个数字进行操作。

思路分析

要使数组的元素之和最小,我们需要尽量减少在每次操作后增加的元素值。 可以观察到: 偶数处理:偶数经过操作后,会变成 x * 2 + 1,这个值会比原始值大很多。 奇数处理:奇数经过操作后,会变成 x * 2,这个变化相对较小。 因此,在每次操作时,我们应优先选择 最小的数字 进行操作,因为对最小的数字进行处理能够有效控制增大的幅度。我们可以使用 最小堆(Min-Heap) 来实现这个策略。最小堆能够在每次操作时快速地找出当前数组中的最小值。

解决方案步骤

初始化最小堆:将数组转化为最小堆。 执行 k 次操作:每次操作都从堆中取出最小元素,并根据其奇偶性进行修改,然后将修改后的元素重新放回堆中。 计算数组和:经过 k 次操作后,计算堆中所有元素的和。

代码实现


def solution(n: int, k: int, a: list) -> int:
    # 将数组转换为最小堆
    min_heap = a[:]
    heapq.heapify(min_heap)
    
    # 执行 k 次操作
    for _ in range(k):
        # 取出堆顶最小的元素
        min_value = heapq.heappop(min_heap)
        
        # 根据奇偶性进行操作
        if min_value % 2 == 0:  # 偶数
            new_value = min_value * 2 + 1
        else:  # 奇数
            new_value = min_value * 2
            
        # 将新值重新放入堆中
        heapq.heappush(min_heap, new_value)
 
    # 计算最终的数组和
    return sum(min_heap)

# 测试样例
print(solution(5, 3, [1, 2, 3, 5, 2]))  # 应该返回 20
print(solution(3, 2, [7, 8, 9]))  # 应该返回 40
print(solution(4, 4, [2, 3, 5, 7]))  # 应该返回 33

思路细化

最小堆的应用:

堆的特性保证了每次弹出的是当前最小的元素,适合我们每次操作时都需要选取最小元素的需求。 操作逻辑:

对于每个数字:

如果是偶数,操作会将其增大,因此我们希望尽量避免对大的偶数进行操作。 如果是奇数,操作只会将其乘以 2,增大的幅度相对较小。

时间复杂度:

将数组转化为堆的时间复杂度为 O(n)。 每次执行堆操作(弹出最小元素并插入新值)的时间复杂度为 O(log^n),总共执行 k 次操作,因此总的时间复杂度为 O(k log^n)。

学习总结

堆的应用:

本题通过堆来实现选择最小值的需求,堆是处理类似“选择最小或最大元素”问题的高效数据结构。在实际编程中,堆的应用场景非常广泛,尤其是在需要频繁获取最大/最小元素时,可以极大地提高效率。

贪心策略:

本题的核心思想是贪心的选择当前最小的元素进行操作。每次操作最小元素,可以最大限度地减少后续增加的值,符合最小化的目标。

对比其他算法:

虽然本题看似可以通过暴力穷举所有方案来实现,但这种方法的时间复杂度将非常高。通过堆的数据结构,能够有效地在较短的时间内找到最小值,从而减少了复杂度。

解题计划

深入学习堆的应用:

在接下来的学习中,加强对堆的理解,尤其是堆排序、优先队列等应用。 练习使用堆来解决其他类似问题,如合并K个有序链表、处理动态流的最大/最小值等问题。

贪心算法:

贪心算法是解决此类问题的常见策略,后续继续学习如何结合堆与贪心策略解决更复杂的问题。

时间复杂度优化:

在分析问题时,进一步关注时间复杂度的优化方法,尽量通过合理选择数据结构和算法来提高程序的效率。