解题思路:最少步数归零问题
解决该问题的核心在于如何通过最少的删除操作,将数组中每个数字逐步归零。以下是具体解题思路的逐步推导:
一、问题理解
首先需要理解题目中的关键点:
- 删除操作:每次可以从一个数字中删除一位。
- 目标:通过最少的删除操作,使数组中的所有数字最终变为
0。 - 优化目标:尽可能减少删除次数。
示例分析
- 数字
103可以通过以下删除操作逐步归零:- 删除第1位:
103 -> 03 -> 3 -> 0,需要 3 次。 - 删除第2位:
103 -> 13 -> 3 -> 0,需要 3 次。 - 删除第3位:
103 -> 10 -> 1 -> 0,需要 3 次。
- 删除第1位:
无论删除顺序如何,一个数字的位数决定了归零所需的最少步数。
二、问题分解与思路推导
1. 核心目标
将数字逐步归零的核心问题是:如何通过最优次序的删除操作,减少整体的删除步数。
- 对每个数字,删除的顺序会影响中间状态,但最终需要归零的总步数与每次删除后的数字“剩余难度”密切相关。
- 使用数字的各位和(
digit sum)来衡量数字的“剩余难度”。 - 优先处理当前“最容易归零”的数字,逐步消耗删除次数。
2. 贪心策略
为了减少总步数,我们采用如下贪心策略:
- 按难度优先级处理数字:
- 每次优先处理 digit sum 最小的数字,即最容易归零的数字。
- 逐步归零:
- 每次删除一位数字后,重新评估剩余数字的难度。
- 动态调整优先级:
- 使用最小堆维护数字的优先级,确保每次从当前最容易归零的数字开始操作。
三、贪心法与堆的结合
1. 为什么选择贪心法?
- 每次优先处理 digit sum 最小的数字,能够快速减少总步数。
- 每步的局部最优解(删除最容易的数字)最终能够带来全局最优解(总步数最少)。
2. 为什么用最小堆?
- 最小堆动态维护当前数组中数字的“最小难度”,保证每次操作选取最优对象。
- 每次删除一位后,将更新后的数字重新加入堆。
四、解题方法总结
核心思路
- 计算每个数字的初始难度(digit sum),并加入最小堆。
- 每次从堆中取出难度最小的数字,执行删除操作。
- 删除后更新数字的难度,并将其重新加入堆(如果未归零)。
- 重复操作直至所有数字归零。
五、算法设计与伪代码
算法设计
- 将所有数字及其初始难度(digit sum)存入最小堆。
- 每次取出堆顶数字,删除其中一位。
- 若数字未归零,则更新其难度并重新放回堆中。
- 记录操作次数,直到堆为空。
伪代码
- 初始化最小堆 heap,将所有数字及其 digit sum 加入堆。
- 初始化操作计数器 steps = 0。
- 当堆不为空时: a. 从堆中取出当前难度最小的数字 num。 b. 如果 num == 0,跳过。 c. 删除 num 的某一位,使其 digit sum 最小化。 d. 更新操作次数 steps += 1。 e. 如果 num 未归零,将更新后的数字重新加入堆。
- 返回操作计数器 steps。
代码
def solution(n: int, a: list) -> int:
def digit_sum(x):
"""Calculate the sum of digits of a number."""
return sum(int(d) for d in str(x))
steps = 0
# Use a min-heap to always process the smallest digit-sum first.
import heapq
heap = []
# Add each number and its digit sum to the heap.
for num in a:
heapq.heappush(heap, (digit_sum(num), num))
while heap:
# Extract the smallest digit-sum number.
_, num = heapq.heappop(heap)
if num == 0:
continue
# Find the digit that gives the largest reduction in digit sum and remove it.
str_num = str(num)
min_digit_sum = float('inf')
best_num = None
for i in range(len(str_num)):
# Remove the i-th digit and calculate the new number.
new_num = int(str_num[:i] + str_num[i + 1:]) if str_num[:i] + str_num[i + 1:] else 0
new_digit_sum = digit_sum(new_num)
if new_digit_sum < min_digit_sum:
min_digit_sum = new_digit_sum
best_num = new_num
# Update the number to the best reduced version and push it back to the heap.
steps += 1
if best_num > 0:
heapq.heappush(heap, (digit_sum(best_num), best_num))
return steps