问题描述
小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
解题思路
解决这个问题的关键在于理解数组操作对总和的影响以及如何通过计算数组总和的因子来优化代码的时间复杂度。
首先,值得注意的是,数组中的操作不会改变数组的总和。即使通过加法和移除元素,所有元素的总和 始终保持不变。这意味着,最终希望所有元素相等的值必须是总和的因子。
因此,我们可以从计算数组总和的因子入手。编写一个函数来处理这一过程是第一步。以下是计算因子的代码实现:
import math
def factor(n:int) -> list:
res = []
for i in range(1, int(math.sqrt(n)+1)):
if n % i == 0:
res.append(n//i)
res.append(i)
return res
接下来的步骤是为每个因子计算所需的操作次数。我们可以采用滑动窗口的思想,从数组的起始位置扫描到结束位置,维护一个左右下标表示当前考察的段落。
具体而言,对于当前因子,我们将根据其值判断如何调整左右下标:
- 小于因子的一段:如果当前段落的总和小于因子,我们将右下标加一,继续扩展当前段落。
- 等于因子的一段:如果当前段落的总和正好等于因子,我们记录下这个段落的长度,并更新左下标为当前右下标,计算需要的合并代价,即该段长度减去一。
- 大于因子的一段:如果当前段落的总和大于因子,则可以直接判断该因子无法作为目标,因为不可能通过操作使得当前段落变为该因子。
为了提高效率,我们可以使用前缀和来快速计算任意段落的总和。下面是完整的解决方案代码::
import math
def factor(n:int) -> list:
res = []
for i in range(1, int(math.sqrt(n)+1)):
if n % i == 0:
res.append(n//i)
res.append(i)
return res
def solution(n: int, a: list) -> int:
s = [0] * (n+1)
for i in range(n):
s[i+1] = s[i] + a[i]
def check(val: int, n: int) -> tuple[bool, int]:
last, res = 0, 0
for i in range(1, n+1):
if s[i] - s[last] == val:
res += i - last - 1
last = i
elif s[i] - s[last] > val:
return False, 0
if n != last :
return False, 0
return True, res
factors = factor(sum(a))
ans = n
for i in factors:
aa, bb = check(i, n)
if aa:
ans = min(ans, bb)
return ans
if __name__ == '__main__':
print(solution(5, [1, 4, 2, 3, 5]) == 2)
print(solution(4, [3, 3, 3, 3]) == 0)
print(solution(6, [1, 1, 1, 2, 2, 2]) == 5)
总结
通过上述分析和实现,我们可以看到解决这个问题的关键在于以下几个方面:
- 数组总和保持不变:操作过程中的数组总和不变,使得最终相等的数必须是数组总和的因子,这一性质极大地简化了问题的复杂度。
- 检查函数的设计:通过编写函数对每个因子进行检查,并使用滑动窗口的方式来计算所需的操作次数,能够有效提高解题效率。
- 因子的利用:对每个因子进行检查可以让我们找出最小的操作次数,从而确保小C以最优的方式达到目标。
总的来说,通过深入理解问题的性质并巧妙运用数据结构,我们能够有效地找到解决方案。这不仅提升了编程技巧,也加深了对算法设计的理解与掌握。