题目描述

✅ 561. 数组拆分

image-20260929110025437

image-20260929110025622

题意分析

将数组中的全部元素两两配对,使每对较小值的总和最大。数组长度为偶数,每个元素必须使用一次;数值可以为负,不能为了增大结果而丢弃某些元素。

解法:排序后相邻配对

核心思路

[!blue]

每一对只有较小值计入答案,较大值相当于没有直接贡献。排序后把相邻元素配对,可以让较大的有效贡献尽量保留下来;这个结论可以通过交换两组来证明。

设剩余元素的最大值为 b、次大值为 a。如果它们已经成对,无需调整;否则设它们分别与 x、y 配对。因为 x、y 都不大于 a,原来两组的贡献为 x + y。改成 (a, b) 与 (x, y) 后,贡献为 a + min(x, y),而 a >= max(x, y),所以新贡献不小于原贡献,其他组完全不受影响。

因此总能把一个最优方案调整成“最大的两个数成对”。移除这两个数,对剩余元素重复相同论证,就得到升序排序后相邻两项成对的最优方案。每对中左侧较小,所以累加下标 0、2、4、... 即可,无需实际构造各个数对。

证明只用到了大小关系,不依赖数值为正,也允许重复元素。因此遇到负数仍正常相加,最终答案可以为负。

解题步骤

  1. 对数组进行升序排序。
  2. 从下标零开始,每次跨过两个元素,把当前位置的值加入总和。
  3. 遍历结束后返回总和,它恰好是所有相邻数对的较小值之和。

代码实现

class Solution {
    public int arrayPairSum(int[] nums) {
        Arrays.sort(nums);
        int sum = 0;

        for (int i = 0; i < nums.length; i += 2) {
            // 排序后相邻两项成组,偶数下标正是每组较小值。
            sum += nums[i];
        }

        return sum;
    }
}
import "sort"

func arrayPairSum(nums []int) int {
    sort.Ints(nums)
    sum := 0
    for i := 0; i < len(nums); i += 2 {
        // 排序后相邻两项成组,偶数下标正是每组较小值。
        sum += nums[i]
    }
    return sum
}

复杂度分析

  • 时间复杂度:$O(N\log(N+1))$,其中 N 为数组长度。排序占主要成本,随后扫描为 $O(N)$。
  • 空间复杂度:求和部分为 $O(1)$,排序工作区依标准库实现而定;输入数组会被原地重排。

关键点总结

[!green]

  • 交换论证比较的是两组总贡献,而不是单独让某一组的较小值最大。
  • 最大两数可以安全成对,重复这一结论就得到相邻配对。
  • 偶数下标是排序后每组的较小值,负数也参与总和。

易错点总结

[!yellow]

  • 未排序就取偶数下标,没有得到最优配对的依据。
  • 升序排序后累加奇数下标,取到的是各组较大值。
  • 首尾配对会让较大的数与很小的数绑定,可能损失本可保留的较小值贡献。
  • 把负答案截为零违反全部元素必须参与配对的要求。

相似题目

题目 难度 关联与区别
1877. 数组中最大数对和的最小值 中等 同样把全部元素两两配对,原题最小化最大数对和而倾向首尾配,本题最大化较小值总和而配相邻项。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/55334195
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!