【C/C++】2321. 拼接数组的最大分数

174 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情


题目链接:2321. 拼接数组的最大分数

题目描述

给你两个下标从 0 开始的整数数组 nums1nums2 ,长度都是 n

你可以选择两个整数 leftright ,其中 0 <= left <= right < n ,接着 交换 两个子数组 nums1[left...right]nums2[left...right]

  • 例如,设 nums1 = [1,2,3,4,5]nums2 = [11,12,13,14,15] ,整数选择 left = 1right = 2,那么 nums1 会变为 [1,12,13,4,5]nums2 会变为 [11,2,3,14,15] 。 你可以选择执行上述操作 一次 或不执行任何操作。

数组的 分数sum(nums1)sum(nums2) 中的最大值,其中 sum(arr) 是数组 arr 中所有元素之和。

返回 可能的最大分数

子数组 是数组中连续的一个元素序列。arr[left...right] 表示子数组包含 nums 中下标 leftright 之间的元素(含 下标 leftright 对应元素)。

提示:

  • n==nums1.length==nums2.lengthn == nums1.length == nums2.length
  • 1n1051 \leqslant n \leqslant 10^5
  • 1nums1[i],nums2[i]1041 \leqslant nums1[i], nums2[i] \leqslant 10^4

示例 1:

输入:nums1 = [60,60,60], nums2 = [10,90,10]
输出:210
解释:选择 left = 1right = 1 ,得到 nums1 = [60,90,60] 和 nums2 = [10,60,10] 。
分数为 max(sum(nums1), sum(nums2)) = max(210, 80) = 210

示例 2:

输入:nums1 = [20,40,20,70,30], nums2 = [50,20,50,40,20]
输出:220
解释:选择 left = 3right = 4 ,得到 nums1 = [20,40,20,40,20] 和 nums2 = [50,20,50,70,30] 。
分数为 max(sum(nums1), sum(nums2)) = max(140, 220) = 220

示例 3:

输入:nums1 = [7,11,13], nums2 = [1,1,1]
输出:31
解释:选择不交换任何子数组。
分数为 max(sum(nums1), sum(nums2)) = max(31, 3) = 31

整理题意

题目给定两个长度相同的数组,我们可以对这两个数组进行一次操作(或者不操作),使得两个数组中任意一个的数组元素和最大。可执行的操作为选取两个数组相同下标且连续的子数组进行交换。

解题思路分析

  • 首先观察题目数据范围,数组长度在 10510^5 以内,暴力枚举所有连续子数组会 TLE 超时。 我们设两数组交换的连续子数组区间为 [left, right],那么对于数组 nums1 来说,设 nums1 在未交换之前的数组和为 sum1,其交换后数组和为:
  • sum1 - (nums1[left] + ... + nums1[right]) + (nums2[left] + ... + nums2[right]) 我们对上式子进行整理,合并 nums1nums2 相同下标部分,整理得:
  • sum1 + (nums2[left] - nums1[left]) + ... + (nums2[right] - nums1[right]) 我们令 diff1[i] = nums2[i] - nums1[i] 可得:
  • sum1 + diff1[left] + ... + diff1[right] 观察这个式子可知,题目要求 sum1 + diff1[left] + ... + diff1[right] 的最大值,因为 sum1 为定值,那么即求 diff1[left] + ... + diff1[right] 的最大值,我们可以通过遍历求得 diff1 数组。

那么问题进而转换为:求 diff1 数组的 最大连续子数组 ,即可求得交换数组后 nums1 数组和的最大值。

我们同理可得 diff2 数组以及 diff2 数组的 最大连续子数组 ,进而得知交换数组后 nums2 数组和的最大值。

那么答案就是二者取最大值即可。

求一个数组的 最大连续子数组 问题为 53. 最大子数组和,具体方法为使用动态规划以时间复杂度为 O(n)O(n) 递推求得。

具体实现

由于该问题可以通过相同的方法求得 diff1diff2 的最大连续子数组和,进而求得交换数组后 nums1nums2 数组和的最大值,那么我们可以将代码优化为一个函数,该函数可以求得数组交换后的数组和最大值。那么在解决相同问题时就可以进行复用。

在求 diff 数组的最大连续子数组和时利用滚动数组思想进行空间优化。

复杂度分析

  • 时间复杂度:O(n)O(n),n 为数组 nums1nums2 中的元素个数,通过一次遍历可求得数组 diff1diff2,再分别遍历数组 diff1diff2,求得交换数组后 nums1nums2 数组和的最大值。
  • 空间复杂度:O(1)O(1)。利用滚动数组思想将空间复杂度降低为 O(1)O(1)

代码实现

class Solution {
private:
    //求得交换数组后的最大数组和(对于该函数的nums1来说的最大数组和)
    int f(vector<int>& nums1, vector<int>& nums2){
        //求原数组 nums1 的数组和
        int sum = 0;
        int n = nums1.size();
        //求 diff 数组的连续子数组和的最大值,利用滚动数组思想优化
        int dp = 0, res = 0;
        for(int i = 0; i < n; i++){
            sum += nums1[i];
            int diff = nums2[i] - nums1[i];
            dp = max(dp + diff, diff);
            res = max(res, dp);
        }
        return sum + res;
    }
public:
    int maximumsSplicedArray(vector<int>& nums1, vector<int>& nums2) {
        return max(f(nums1, nums2), f(nums2, nums1));
    }
};

总结

  • 该题的核心为 求数组的最大连续子数组和,只是题目将该其隐藏的比较好,需要我们将问题进行转换。
  • 在面临同理可得这一结论时,我们可以考虑代码的复用性,将同理部分代码进行封装写成函数,在求相同问题时可以进行复用。
  • 本文对 求数组的最大连续子数组和 没有详细讲述,仅进行了方法概括,因为本题核心是如何将问题转换为求数组的最大连续子数组和。详细求解 求数组的最大连续子数组和 的方法可以在 53. 最大子数组和 中进行查看。
  • 测试结果:

2321.png

结束语

无论做什么事,都不可能一蹴而就。阅读书籍,没有立竿见影的好处,却会给你广阔的视野;培养好习惯,过程并不容易,却能让你持久获益。别因为短期看不到回报就拒绝努力,当下的每一点付出,都会在未来助你成为更耀眼的自己。新的一天,加油!