题目解析:小S的最小化数组分数问题 | 豆包MarsCode AI刷题

0 阅读5分钟

小S的最小化数组分数问题


一、问题分析

小S有一个整数数组 nums 和一个整数 k,他可以对数组中的每个元素 nums[i] 进行调整,每次调整可以选择增加 k 或减少 k,即 nums[i] + knums[i] - k。经过这些操作后,数组的分数定义为调整后的数组中最大值与最小值的差值。问题的目标是通过对数组进行合适的调整,使得最终的分数尽可能小。

从问题本质来看,数组的分数完全由其最大值和最小值决定。调整的关键在于尽可能缩小这两个值之间的差距,同时保证操作覆盖数组中的每个元素。这种调整具有多种可能性,因此暴力枚举所有情况显然不可行。我们需要找到一种高效的方法,在有限的计算时间内找到最优的调整方案。


二、思路解析

为了简化问题并找到最优解,我们可以先对数组 nums 进行升序排序。排序后的数组从小到大排列,使得最大值和最小值的位置固定为数组的两端,即 nums[0] 为最小值,nums[-1] 为最大值。这种排列方式使得调整的目标变得更加明确,即通过对数组进行合理分割,使一部分元素尽量增加,另一部分元素尽量减少,从而最大限度地缩小两端差距。

初始情况下,数组的分数为未调整时的最大值与最小值的差,即 nums[-1] - nums[0]。这个值是未调整时的结果,也是后续优化的起点。在调整过程中,我们可以选择将数组分为两部分,前一部分的元素统一增加 k,后一部分的元素统一减少 k。调整后,数组的最大值和最小值分别可能来自两部分的极端情况:最大值为 max(nums[i] + k, nums[-1] - k),最小值为 min(nums[0] + k, nums[i + 1] - k)。通过枚举分割点,将上述公式应用于所有可能的分割情况,我们可以找到最优的调整方案。

在计算过程中,我们动态更新最小分数的值。通过比较每种分割方式下的最大值与最小值的差,保留最小的结果作为最终答案。由于数组经过排序,最大值和最小值的计算仅涉及局部元素,因此整个过程可以高效完成。最终,我们返回计算出的最小分数,确保结果为非负值。


三、代码详解

def solution(nums: list, k: int) -> int:
    # 对数组进行排序
    nums.sort()
    n = len(nums)
    # 初始化最小分数为未调整时的最大值与最小值的差
    ans = nums[-1] - nums[0]
    # 遍历所有分割点
    for i in range(n - 1):
        # 计算分割后数组的最大值和最小值
        high = max(nums[i] + k, nums[-1] - k)
        low = min(nums[0] + k, nums[i + 1] - k)
        # 更新最小分数
        ans = min(ans, high - low)
    # 返回最终结果
    return ans if ans >= 0 else 0

这段代码的核心逻辑是通过以下步骤实现问题的高效解决:首先,对数组 nums 进行排序,以固定元素的排列顺序,确保在后续处理时最大值和最小值始终位于明确的位置(排序后的两端)。排序的作用不仅是简化逻辑,还能帮助我们快速找到调整后的可能极值范围。对于一个经过排序的数组,调整后最大值和最小值的候选值范围会显著缩小,从而避免对每个可能的组合逐一计算。

在排序完成后,代码通过遍历数组中的分割点(索引 i)来动态调整可能的最大值和最小值范围。对于每个分割点,数组被分成两部分:前 i+1 个元素尝试通过增加 k 来缩小范围,而后续元素通过减少 k 来优化极值。这种分割方式确保了不同调整策略的充分尝试,同时将问题复杂度限制在合理范围内。

具体而言,在每个分割点下,通过计算增加 k 和减少 k 后的可能最大值 high 和最小值 low,我们能够快速确定调整后的分数,即 high - low。每次计算后,更新当前的最小分数,从而动态追踪最优结果。最终,代码输出遍历过程中找到的最小分数,这一结果代表了所有调整方式中最优的情况。这种方法不仅逻辑清晰,而且在时间复杂度上实现了从暴力求解到高效算法的转变,使其适用于更大规模的输入数据。

四、总结

通过排序和分割的策略,该算法有效地解决了数组分数最小化问题。排序后的数组通过分割点的枚举,将全局问题简化为局部问题,每次只需计算两端的极值。最终,通过动态更新最小分数的值,能够快速找到最优解。这种方法避免了暴力穷举的高时间复杂度,同时在逻辑上清晰易懂,是处理类似问题的高效方案。