79.翻转增益的最大子数组和<字节青训营-简单题>

1.题目

问题描述

小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

2.思路

  1. 穷举,找到所有的翻转方式,然后求最大子数组和
  2. 假设通过翻转可以把所有正数放在一起,求所有正数的和即可,如果没有正数,答案为最大的负数
  3. 初始化动态规划数组
    • ldp 数组用于存储从左到右的最大子数组和。
    • rdp 数组用于存储从右到左的最大子数组和。
  4. 计算从左到右的最大子数组和
    • 遍历数组,累加当前元素到 s,如果 s 小于 0,则重置 s 为 0。
    • 更新 ldp[i] 为 ldp[i-1] 和 s 的较大值,如果 i 为 0,则直接取 max(0, data_array[i])
  5. 计算从右到左的最大子数组和
    • 反向遍历数组,累加当前元素到 rdp[i],更新 rdp[i] 为当前元素和 rdp[i+1] + data_array[i] 的较大值,如果 i 为 N-1,则直接取 data_array[i]
  6. 计算翻转后的最大子数组和
    • 遍历数组,计算 ldp[i] + rdp[i+1] 的最大值,并与数组中的最大元素比较,取较大值作为结果。

3.代码

思路二代码,经验证是错误的

#include <iostream>
#include <vector>
using namespace std;

int solution(int N, std::vector<int> data_array) {
  // Please write your code here
  bool has_positive = false;
  int ans = 0;
  int max_num = 0; //记录最大的数
  for (int i = 0; i < data_array.size(); i++) {
    max_num = max(max_num, data_array[i]);
    if (data_array[i] > 0) {
      ans += data_array[i];
      has_positive = true;
    }
  }
  if (has_positive == true) {
    return ans;
  } else {
    return max_num;
  }
}

int main() {
  // You can add more test cases here
   std::vector<int> array1 = {
    1,2,3,-1,4
   };
  std::vector<int> array2 = {
      -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, array1) == 10) << std::endl;
  std::cout << (solution(100, array2) == 1348) << std::endl;
  return 0;
}
def solution(N, data_array):
    # Please write your code here
    assert N == len(data_array)
    ldp = [0] * N # 用于存储从左到右的最大子数组和
    rdp = [0] * N # 用于存储从右到左的最大子数组和

    s = 0 # 当前子数组和
    for i in range(N):
        s += data_array[i]
        if s < 0:
            s = 0 # 如果子数组和为负,重置为0
        ldp[i] = max(ldp[i - 1], s) if i != 0 else max(0, data_array[i]) # 更新从左到右的最大子数组和
    for i in range(N - 1, -1, -1):
        rdp[i] = max(data_array[i], rdp[i + 1] + data_array[i]) if i != N - 1 else data_array[i]
    
    ans = float('-inf')
    for i in range(N - 1):
        ans = max(ans, ldp[i] + rdp[i + 1]) # 计算翻转后的最大子数组和
    return max(ans, max(data_array))

if __name__ == "__main__":
    #  You can add more test cases here
    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]
    print(solution(5, [1,2,3,-1,4]) == 10 )
    print(solution(100, array1) == 1348)

ldp[i] = max(ldp[i - 1], s) if i != 0 else max(0, data_array[i]) # 更新从左到右的最大子数组和

  • if i != 0:判断是否是第一个元素。如果不是第一个元素,使用前一个元素的最大子数组和 ldp[i - 1] 和当前子数组和 s 来更新当前的最大子数组和。
    • max(ldp[i - 1], s)ldp[i - 1] 代表前一个位置的最大子数组和,s 是当前子数组和。如果当前子数组和 s 比前一个位置的最大和 ldp[i - 1] 更大,就更新为 s
  • else max(0, data_array[i]):如果是第一个元素,ldp[0] 的值应该是 data_array[0] 或者 0(如果 data_array[0] 为负,则 0 更好)。这意味着在处理第一个元素时,如果它为负数,我们选择跳过它,开始新的子数组。

rdp[i] = max(data_array[i], rdp[i + 1] + data_array[i]) if i != N - 1 else data_array[i]

  • if i != N - 1:判断是否是最后一个元素。如果 i != N - 1,则说明当前位置不是数组的最后一个元素。此时,rdp[i] 可以是当前元素 data_array[i] 本身,或者是将当前元素 data_array[i] 与之后的子数组和 rdp[i + 1] 结合,取这两者中的较大值。
    • max(data_array[i], rdp[i + 1] + data_array[i])
      • data_array[i]:当前元素自身,表示如果选择从当前元素开始新的子数组(即舍弃之前的子数组)。
      • rdp[i + 1] + data_array[i]:表示将当前位置的元素加到之后的子数组和中,继续扩展当前的子数组。如果这样做会导致当前的子数组和更大,那么选择这个子数组。
  • else data_array[i]:如果是最后一个元素(即 i == N - 1),则 rdp[i] 只能是当前元素本身,因为没有更右边的元素可以加入。因此,rdp[N-1] = data_array[N-1]

ans = max(ans, ldp[i] + rdp[i + 1])

通过翻转子数组,可能将 ldp[i]rdp[i + 1] 两个部分拼接起来,从而得到一个新的子数组。我们考虑从数组中选择一个区间,翻转该区间后,使得翻转后的子数组的和变得最大。具体步骤如下:

  • ldp[i] 表示左侧部分(从数组的开头到位置 i)的最大子数组和。
  • rdp[i + 1] 表示右侧部分(从位置 i + 1 到数组末尾)经过翻转后的最大子数组和。
  • 因此,ldp[i] + rdp[i + 1] 表示翻转 ii + 1 之间的部分后,得到的新的子数组和。

return max(ans, max(data_array))

确保函数返回的结果是最大可能的子数组和,即使最好的选择是不翻转任何子数组。以下是这行代码的详细解释:

  1. max(data_array):这一部分找到数组中的最大单个元素。这在所有元素都为负数的情况下非常有用,因为此时最好的子数组和将是最大的单个元素。
  2. max(ans, max(data_array)):这一部分将通过考虑所有可能的子数组翻转找到的最大子数组和(ans)与数组中的最大单个元素进行比较。它确保函数返回最高的可能值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值