AI刷题翻转增益的最大子数组和--问题转化

125 阅读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

问题转化

如果不删除元素的话,求最大子数组和我们都接触过,经典动态规划,dp[i]为以i结尾的子数组和最大值,我们想想能不能仿照这个思路?

假设某个数组翻转过后能取得较大的子数组和,那么这个子数组的边界一定在这个翻转数组中,换言之就是这个翻转数组中的一些数字一定不选。不选的操作我们很自然就能想到删除,所以可以试试能不能把翻转操作改成删除操作

按照示例[1, 2, 3, -1, 4], 有两种翻转策略,第一种令-1,4为翻转数组,然后不选-1, 第二种令1, 2, 3, -1为翻转数组,然后不选-1, 这两种翻转是等效的。所以我们可以假设删除的一定是翻转数组的末尾一定长度的数据, 我们不妨枚举翻转数组的末位i,然后计算删除翻转数组某尾一段长度后的最大子数组和

因为删除操作是要连续的,并且一旦删除完成后不能再次删除,所以我们可以设置3个状态, 没有删除,正在删除和已经完成删除

所以,翻转后的最大数组和可以看作: 删除连续的数组后的子数组和, (也就是从被删除的最后一个元素处翻转,翻转长度任意)

状态DP

dp[i][j]表示以i结尾,状态为j的最大数组和

  • j==0, 表示不删除数字的最大子数组
  • j==1,表示刚刚好删到i的最大子数组,(还可以继续删)
  • j==2,表示已经完成删除操作的最大子数组,(不可以继续删)
#include <iostream>
#include <numeric>
#include <vector>
const int INT_MIN = -100000;
int solution(int N, std::vector<int> data_array) {
  // Please write your code here
  std::vector<std::vector<int>> dp(N+1, std::vector<int>(3,INT_MIN));
  int temp = INT_MIN;
  int ans = INT_MIN;
  for(int i = 0; i<N; i++)
  {
    dp[i+1][1] = temp + data_array[i];//刚刚好删除到i的最大子数组转移方程,就是i之前的最大子数组加i
    dp[i+1][0] = dp[i][0] > 0 ? dp[i][0]+data_array[i] : data_array[i];//不删除数字的最大子数组
    temp = std::max(dp[i+1][0], temp);
    dp[i+1][2] = std::max(dp[i][2],dp[i][1])+data_array[i];//删除完成的最大子数组
    ans = std::max(dp[i+1][0],std::max(dp[i+1][1], std::max(dp[i+1][2], ans)));
  }
  
  return ans;
}

由于dp[i+1]只与dp[i]有关,所以我们可以进行空间优化,省去个维度,引入pre变量维护dp[i][1]的值

int solution(int N, std::vector<int> data_array) {
    vector<int> dp(3, INT_MIN); // dp[0]: 不删除,dp[1]: 刚好删除,dp[2]: 删除完成
    int temp = INT_MIN;
    int pre = INT_MIN;// 保存 dp[1] 的值
    int ans = INT_MIN; 

    for (int i = 0; i < N; i++) {
        dp[1] = temp + data_array[i]; // 删除完成的最大子数组
        dp[0] = (dp[0] > 0 ? dp[0] + data_array[i] : data_array[i]); // 不删除
        temp = max(dp[0], temp); // 更新 temp 为 dp[0] 的最大值
        dp[2] = max(dp[2], pre) + data_array[i]; //删除完成的最大子数组
        pre = dp[1]; // 更新 pre 为当前的 dp[1]
        ans = max({dp[0], dp[1], dp[2], ans});
    }
    return ans;
}