[路飞]_前端算法第一零三弹-数组拆分 I

100 阅读2分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战

给定长度为 2n ****的整数数组 nums ,你的任务是将这些数分成 n ****对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从 1 到 nmin(ai, bi) 总和最大。

返回该 最大总和

示例 1:

输入:nums = [1,4,3,2]
输出:4
解释:所有可能的分法(忽略元素顺序)为:
(1, 4), (2, 3) -> min(1, 4) + min(2, 3) = 1 + 2 = 3
(1, 3), (2, 4) -> min(1, 3) + min(2, 4) = 1 + 2 = 3
(1, 2), (3, 4) -> min(1, 2) + min(3, 4) = 1 + 3 = 4
所以最大总和为 4

示例 2:

输入:nums = [6,2,6,5,1,2]
输出:9
解释:最优的分法为 (2, 1), (2, 5), (6, 6). min(2, 1) + min(2, 5) + min(6, 6) = 1 + 2 + 6 = 9

思路与算法

不失一般性,我们令每一组 (ai,bi)(ai,bi) 满足 aibiai≤bi(若不满足,交换二者即可),这样我们需要求得的总和min(ai,bi)min(ai,bi)求和就等于所有 aiai 的和。

接下来,我们将所有的 (ai,bi) 按照升序排序,使得 a1a2ana1≤a2≤⋯≤an。这样一来,对于任意的 ajaj

  • 它不大于 aj+1,aj+2,,anaj+1,aj+2,⋯ ,an
  • 它不大于 bjbj
  • 由于 aibiai≤bi 对于任意的 ii 恒成立,因此它不大于 bj+1,bj+2,,bnbj+1,bj+2,⋯ ,bn

由于 ajaj 不大于 {aa} 中的 njn−j 个元素,也不大于 {bb} 中的 nj+1n−j+1 个元素,而这些元素都是从 numsnums 中而来的,因此 ajaj 在数组 numsnums 中「从大到小」至少排在第 (nj)+(nj+1)+1=2(nj+1)(n−j)+(n−j+1)+1=2(n−j+1) 个位置,也就是「从小到大」至多排在第 2n2(nj+1)+1=2(j1)+12n−2(n−j+1)+1=2(j−1)+1 个位置,这里位置的编号从 1 开始,即

图片.png

其中数组 c 是将数组 nums 升序排序得到的结果,代入 aiai求和 式即可得到

图片.png

另一方面,令 (a1,b1)=(c1,c2),(a2,b2)=(c3,c4),,(an,bn)=(c2n1,c2n)(a1,b1)=(c1,c2),(a2,b2)=(c3,c4),⋯ ,(an,bn)=(c2n−1,c2n),此时每一组 (ai,bi)(ai,bi) 都满足 aibiai≤bi 的要求,并且有 a1a2ana1≤a2≤⋯≤an,此时

图片.png

即 (2) 式的等号是可满足的。因此所要求得的最大总和即为

图片.png

var arrayPairSum = function(nums) {
    nums.sort((a, b) => a - b);
    let ans = 0;
    for (let i = 0; i < nums.length; i += 2) {
        ans += nums[i];
    }
    return ans;
};

复杂度分析

  • 时间复杂度:O(nlogn)O(nlog⁡n),即为对数组 numsnums 进行排序的时间复杂度。
  • 空间复杂度:O(nlogn)O(nlog⁡n),即为排序需要使用的栈空间。

图片.png