掘金刷题4 | 豆包MarsCode AI刷题

75 阅读4分钟

题目描述

问题描述

小C面对一个由整数构成的数组,他考虑通过一次操作提升数组的潜力。这个操作允许他选择数组中的任一子数组并将其翻转,目的是在翻转后的数组中找到具有最大和的子数组。小C对这个可能性很感兴趣,并希望知道翻转后的数组中可能得到的最大子数组和是多少。

例如,数组是 1, 2, 3, -1, 4。小C可以选择翻转子数组 -1, 4 得到 1, 2, 3, 4, -1 或者翻转 1, 2, 3, -1 得到 -1, 3, 2, 1, 4,在这两种情况下,最大的子数组和都是 10。

备注:子数组 是数组中的一个连续部分。

输入

  • N: 数组的长度
  • data_array: 一个长度为 N 的整数数组

输出

请你返回执行翻转操作后(也可以选择不翻转),数组中可能获得的最大子数组和。


测试样例

样例1:

输入:N = 5,data_array = [1, 2, 3, -1, 4]
输出:10

样例2:

输入:N = 4,data_array = [-3, -1, -2, 3]
输出:3

样例3:

输入:N = 3,data_array = [-1, -2, -3]
输出:-1

样例4:

输入:N = 6,data_array = [-5, -9, 6, 7, -6, 2]
输出:15

样例5:

输入:N = 7,data_array = [-8, -1, -2, -3, 4, -5, 6]
输出:10

思路分析

  1. 计算不翻转的最大子数组和

    • 使用 Kadane 算法计算原始数组的最大子数组和,记为 max_sum_no_flip
  2. 计算翻转后的最大子数组和

    • 翻转子数组意味着我们可以将某个子数组的顺序完全颠倒。我们需要考虑翻转后可能对最大子数组和的影响。
    • 翻转操作可以看作是将某个子数组的前缀和后缀交换位置。因此,我们需要分别计算每个可能的分割点的前缀最大子数组和与后缀最大子数组和。
  3. 合并前缀和后缀的最大子数组和

    • 对于每个分割点 i,我们计算前缀 [0, i-1] 的最大子数组和 prefix_max[i] 和后缀 [i, N-1] 的最大子数组和 suffix_max[i]
    • 然后,计算翻转子数组 [i, j] 后的最大子数组和,即 prefix_max[i] + suffix_max[j]
  4. 综合考虑

    • 最终的最大子数组和可能是原始数组的最大子数组和 max_sum_no_flip 或者翻转后的最大子数组和。

实现步骤

  1. Kadane 算法计算最大子数组和

    • 遍历数组,记录当前子数组的最大和 current_max 和全局最大和 max_sum_no_flip
  2. 计算前缀和后缀的最大子数组和

    • 使用类似 Kadane 算法的思想,分别计算前缀和后缀的最大子数组和。
  3. 计算翻转后的最大子数组和

    • 对于每个分割点,计算前缀和后缀的最大子数组和的和,更新全局最大值。

完整代码

#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;
// 计算最大子数组和的 Kadane 算法
int maxSubArraySum(vector<int>& arr) {
    int max_so_far = INT_MIN, max_ending_here = 0;
    for (int i = 0; i < arr.size(); i++) {
        max_ending_here = max_ending_here + arr[i];
        if (max_so_far < max_ending_here)
            max_so_far = max_ending_here;
        if (max_ending_here < 0)
            max_ending_here = 0;
    }
    return max_so_far;
}


int solution(int N, vector<int>arr) {
        int n = arr.size();
    int originalMaxSum = maxSubArraySum(arr);

    // 计算前缀和
    vector<int> prefixSum(n + 1, 0);
    for (int i = 0; i < n; i++) {
        prefixSum[i + 1] = prefixSum[i] + arr[i];
    }

    int maxSum = originalMaxSum;

    // 尝试所有可能的翻转区间 [i, j]
    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            // 翻转 [i, j] 区间
            vector<int> temp = arr;
            reverse(temp.begin() + i, temp.begin() + j + 1);
            maxSum = max(maxSum, maxSubArraySum(temp));
        }
    }

    return maxSum;
    // Please write your code here
    return -2;
}

int main() {
    // You can add more test cases here
    vector<int>array1 = {-85, -11, 92, 6, 49, -76, 28, -16, 3, -29, 26, 37, 86, 3, 25, -43, -36, -27, 45, 87, 91, 58, -15, 91, 5, 99, 40, 68, 54, -95, 66, 49, 74, 9, 24, -84, 12, -23, -92, -47, 5, 91, -79, 94, 61, -54, -71, -36, 31, 97, 64, -14, -16, 48, -79, -70, 54, -94, 48, 37, 47, -58, 6, 62, 19, 8, 32, 65, -81, -27, 14, -18, -34, -64, -97, -21, -76, 51, 0, -79, -22, -78, -95, -90, 4, 82, -79, -85, -64, -79, 63, 49, 21, 97, 47, 16, 61, -46, 54, 44};
    //std::cout << (solution(5, new int[5]{1, 2, 3, -1, 4}) == 10) << std::endl;
    std::cout << (solution(100, array1) == 1348) << std::endl;
    return 0;
}