AI 刷题 158. 小C的合并数组问题 题解 | 豆包MarsCode AI刷题

87 阅读4分钟

问题描述

小C有一个长度为 n 的数组。她可以执行以下操作:选择一个下标 i,将 a_i 加到 a_{i-1} 或者 a_{i+1}(如果相邻的下标在范围内),然后移除元素 a_i。她想知道,最少需要进行多少次操作,才能使数组的所有元素相等。


测试样例

样例1:

输入:n = 5 , a = [1, 4, 2, 3, 5]
输出:2

样例2:

输入:n = 4 , a = [3, 3, 3, 3]
输出:0

样例3:

输入:n = 6 , a = [1, 1, 1, 2, 2, 2]
输出:5

问题分析:

给定一个长度为 nn 的数组。我们可以进行以下操作:

  • 选择一个下标 ii,将 aia_i 加到 ai−1a_{i-1} 或 ai+1a_{i+1}(如果相邻的下标在范围内),然后移除元素 aia_i。

目标是最少操作次数,使数组的所有元素相等。

关键观察:

  1. 总和不变性: 每次操作后,数组的总和保持不变。因为我们只是将一个元素的值加到相邻元素上,然后删除该元素。
  2. 划分问题转化: 问题可以转化为将数组划分为若干连续的段,使每段的和相等。最少的操作次数为 n−kn - k,其中 kk 是可以划分的段数。
  3. 可能的段和: 这些段的和为数组总和 SS 的因数。

解题思路:

  1. 计算数组总和: 计算 S=∑i=1naiS = \sum_{i=1}^{n} a_i。

  2. 找出 SS 的因数(不超过 nn): 这些因数代表可能的段数 kk。

  3. 尝试每个可能的段数 kk:

    • 计算目标段和 target_sum=S/k\text{target_sum} = S / k。
    • 尝试将数组划分为 kk 个连续段,每段的和为 target_sum\text{target_sum}。
    • 使用贪心算法,累加元素值,当累加和等于 target_sum\text{target_sum} 时重置累加器。
    • 如果成功划分(累加和在 kk 次达到 target_sum\text{target_sum},且不超过),更新最小操作次数为 n−kn - k。
  4. 输出最小操作次数: 在所有可能的 kk 中,选择最小的 n−kn - k。

时间和空间复杂度分析:

  • 时间复杂度:

    • 计算数组总和:O(n)O(n)。
    • 找因数:O(S)O(\sqrt{S}),但由于 n≤105n \leq 10^5,因数数量有限。
    • 对每个因数 kk 进行划分检查:每次 O(n)O(n)。
    • 总时间复杂度:O(n×D)O(n \times D),其中 DD 是因数的数量,通常很小。
  • 空间复杂度:

    • 存储数组:O(n)O(n)。
    • 存储因数集:O(D)O(D)。
    • 总空间复杂度:O(n)O(n)。

代码实现:

def min_operations_to_equalize(n, a):
    total_sum = sum(a)
    max_operations = n - 1  # 最坏情况下,需要进行 n - 1 次操作
    min_operations = max_operations

    # 获取不超过 n 的总和的因数
    def get_divisors(s, n):
        divisors = set()
        for i in range(1, int(s**0.5) + 1):
            if s % i == 0:
                if i <= n:
                    divisors.add(i)
                if s // i <= n:
                    divisors.add(s // i)
        return divisors

    divisors = get_divisors(total_sum, n)

    for k in divisors:
        target_sum = total_sum // k
        current_sum = 0
        valid_partition = True
        for num in a:
            current_sum += num
            if current_sum == target_sum:
                current_sum = 0
            elif current_sum > target_sum:
                valid_partition = False
                break
        if valid_partition and current_sum == 0:
            operations = n - k
            min_operations = min(min_operations, operations)

    return min_operations

# 示例使用:
if __name__ == "__main__":
    # 测试样例
    n1, a1 = 5, [1, 4, 2, 3, 5]
    n2, a2 = 4, [3, 3, 3, 3]
    n3, a3 = 6, [1, 1, 1, 2, 2, 2]

    print(min_operations_to_equalize(n1, a1))  # 输出:2
    print(min_operations_to_equalize(n2, a2))  # 输出:0
    print(min_operations_to_equalize(n3, a3))  # 输出:5

代码说明:

  • 函数 get_divisors 获取不超过 nn 的 SS 的所有因数。
  • 主循环: 遍历每个因数 kk,计算目标段和,并检查能否成功划分。
  • 划分检查: 累加元素值,判断是否能在不超过目标段和的情况下成功划分。
  • 更新最小操作次数: 如果成功划分,更新最小操作次数。

优化过程:

  • 限制因数范围: 只考虑不超过 nn 的因数,减少计算量。
  • 高效的划分检查: 使用贪心算法,一旦累加和超过目标段和,立即停止当前因数的检查,避免不必要的计算。