C语言/数据动态规划题解:翻转一次子数组后的最大子数组和——四状态DP(O(n)时间O(1)空间)

问题描述

小明在玩一个有趣的数字游戏,他有一个整数数组,代表每个数字的"能量值"。游戏允许他选择数组中的任意一个连续子数组,并将这个子数组内的所有数字翻转(正变负,负变正)一次。翻转操作只能执行一次。

翻转后,小明希望整个数组的最大子数组和尽可能大。注意,最大子数组和是指所有连续子数组中元素和最大的那个值。

请你帮助小明设计一个算法,找出在执行最多一次翻转操作后,整个数组可能的最大子数组和。

测试样例

样例1:

输入:nums = [1, -2, 3, -4, 5] 输出:9 解释:翻转连续子数组 -2, 3, -4 后,数组变为 1, 2, -3, 4, 5。此时,最大子数组和是 4+5=9(子数组 4,5)。其他翻转方式得到的最大子数组和均小于9。

样例2:

输入:nums = [-1, -2, -3] 输出:6 解释:翻转整个数组 -1, -2, -3 后,数组变为 1, 2, 3。此时,最大子数组和是 1+2+3=6(整个数组)。如果不翻转,最大子数组和为 -1(子数组 -1 或单个元素)。

样例3:

输入:nums = [1, 2, 3] 输出:6 解释:数组本身已经全为正数,最大子数组和为 1+2+3=6。翻转任意子数组只会引入负数,降低最大子数组和,因此无需翻转。

约束条件

  • 数组长度 n 满足:1 <= n <= 10^5
  • 数组元素为整数,绝对值不超过 10^4

解题思路

要解决这个问题,关键在于理解翻转操作对最大子数组和的影响。翻转一个连续子数组(即将其每个元素符号取反)相当于改变了该段子数组的符号,这可能会创造新的最大子数组和。

常见的方法是使用动态规划来跟踪翻转和不翻转状态下的最大子数组和。具体来说,可以维护三个状态:

  • dp0:当前未进行任何翻转的最大子数组和
  • dp1:当前已经进行过一次翻转的最大子数组和
  • dp2:当前已经结束翻转(即翻转操作已结束)的最大子数组和

通过遍历数组,更新这些状态,最终max(dp0, dp1, dp2)即为所求答案。

这种方法的时间复杂度为 O(n),空间复杂度为 O(1),适用于大规模数据。

参考代码

python 复制代码
def maxSumAfterFlip(nums):
    dp0 = nums[0]
    dp1 = -nums[0]
    dp2 = nums[0]
    res = max(dp0, dp1, dp2)
    for i in range(1, len(nums)):
        num = nums[i]
        new_dp0 = max(num, dp0 + num)
        new_dp1 = max(-num, dp0 - num, dp1 - num)
        new_dp2 = max(num, dp1 + num, dp2 + num)
        dp0, dp1, dp2 = new_dp0, new_dp1, new_dp2
        res = max(res, dp0, dp1, dp2)
    return res
cpp 复制代码
public int maxSumAfterFlip(int[] nums) {
    int dp0 = nums[0];
    int dp1 = -nums[0];
    int dp2 = nums[0];
    int res = Math.max(Math.max(dp0, dp1), dp2);
    for (int i = 1; i < nums.length; i++) {
        int num = nums[i];
        int new_dp0 = Math.max(num, dp0 + num);
        int new_dp1 = Math.max(Math.max(-num, dp0 - num), dp1 - num);
        int new_dp2 = Math.max(Math.max(num, dp1 + num), dp2 + num);
        dp0 = new_dp0;
        dp1 = new_dp1;
        dp2 = new_dp2;
        res = Math.max(res, Math.max(Math.max(dp0, dp1), dp2));
    }
    return res;
}
cpp 复制代码
function maxSumAfterFlip(nums) {
    let dp0 = nums[0];
    let dp1 = -nums[0];
    let dp2 = nums[0];
    let res = Math.max(dp0, dp1, dp2);
    for (let i = 1; i < nums.length; i++) {
        const num = nums[i];
        const new_dp0 = Math.max(num, dp0 + num);
        const new_dp1 = Math.max(-num, dp0 - num, dp1 - num);
        const new_dp2 = Math.max(num, dp1 + num, dp2 + num);
        dp0 = new_dp0;
        dp1 = new_dp1;
        dp2 = new_dp2;
        res = Math.max(res, dp0, dp1, dp2);
    }
    return res;
}
cpp 复制代码
#include <vector>
#include <algorithm>
using namespace std;

int maxSumAfterFlip(vector<int>& nums) {
    int dp0 = nums[0];
    int dp1 = -nums[0];
    int dp2 = nums[0];
    int res = max({dp0, dp1, dp2});
    for (int i = 1; i < nums.size(); i++) {
        int num = nums[i];
        int new_dp0 = max(num, dp0 + num);
        int new_dp1 = max({-num, dp0 - num, dp1 - num});
        int new_dp2 = max({num, dp1 + num, dp2 + num});
        dp0 = new_dp0;
        dp1 = new_dp1;
        dp2 = new_dp2;
        res = max({res, dp0, dp1, dp2});
    }
    return res;
}
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define max(a, b) ((a) > (b) ? (a) : (b))
#define max3(a, b, c) max(max(a, b), c)

int maxSumAfterFlip(int* nums, int numsSize) {
    int dp0 = nums[0];
    int dp1 = -nums[0];
    int dp2 = nums[0];
    int res = max3(dp0, dp1, dp2);
    for (int i = 1; i < numsSize; i++) {
        int num = nums[i];
        int new_dp0 = max(num, dp0 + num);
        int new_dp1 = max3(-num, dp0 - num, dp1 - num);
        int new_dp2 = max3(num, dp1 + num, dp2 + num);
        dp0 = new_dp0;
        dp1 = new_dp1;
        dp2 = new_dp2;
        res = max3(res, max3(dp0, dp1, dp2));
    }
    return res;
}
python 复制代码
func maxSumAfterFlip(nums []int) int {
    dp0 := nums[0]
    dp1 := -nums[0]
    dp2 := nums[0]
    res := max(max(dp0, dp1), dp2)
    for i := 1; i < len(nums); i++ {
        num := nums[i]
        new_dp0 := max(num, dp0 + num)
        new_dp1 := max(-num, max(dp0 - num, dp1 - num))
        new_dp2 := max(num, max(dp1 + num, dp2 + num))
        dp0, dp1, dp2 = new_dp0, new_dp1, new_dp2
        res = max(res, max(max(dp0, dp1), dp2))
    }
    return res
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}
cpp 复制代码
impl Solution {
    pub fn max_sum_after_flip(nums: Vec<i32>) -> i32 {
        let mut dp0 = nums[0];
        let mut dp1 = -nums[0];
        let mut dp2 = nums[0];
        let mut res = dp0.max(dp1).max(dp2);
        for i in 1..nums.len() {
            let num = nums[i];
            let new_dp0 = num.max(dp0 + num);
            let new_dp1 = (-num).max(dp0 - num).max(dp1 - num);
            let new_dp2 = num.max(dp1 + num).max(dp2 + num);
            dp0 = new_dp0;
            dp1 = new_dp1;
            dp2 = new_dp2;
            res = res.max(dp0).max(dp1).max(dp2);
        }
        res
    }
}

程序代码

#include <stdio.h>

#define max(a, b) ((a) > (b) ? (a) : (b))

#define max3(a, b, c) max(max(a, b), c)

int maxSumAfterFlip(int* nums, int numsSize) {

if (numsSize == 1) return nums0;

int NEG = -1000000000;

int dp0 = nums0; // 未翻转

int dp1 = -nums0; // 翻转中,长度=1

int dp2 = NEG; // 翻转中,长度≥2

int dp3 = NEG; // 翻转已结束

int res = max(dp0, dp1);

for (int i = 1; i < numsSize; i++) {

int num = numsi;

int new_dp0 = max(num, dp0 + num);

int new_dp1 = max(-num, dp0 - num);

int new_dp2 = max(dp1 - num, dp2 - num);

int new_dp3 = max3(num, dp2 + num, dp3 + num);

dp0 = new_dp0;

dp1 = new_dp1;

dp2 = new_dp2;

dp3 = new_dp3;

res = max(res, max(max(dp0, dp1), max(dp2, dp3)));

}

return res;

}

int main() {

int nums1\[\] = {1, -2, 3, -4, 5};

int nums2\[\] = {-1, -2, -3};

int nums3\[\] = {1, 2, 3};

printf("%d\n", maxSumAfterFlip(nums1, 5)); // 9

printf("%d\n", maxSumAfterFlip(nums2, 3)); // 6

printf("%d\n", maxSumAfterFlip(nums3, 3)); // 6

return 0;

}

cpp 复制代码
#include <stdio.h>

#define max(a, b) ((a) > (b) ? (a) : (b))
#define max3(a, b, c) max(max(a, b), c)

int maxSumAfterFlip(int* nums, int numsSize) {
    if (numsSize == 1) return nums[0];
    
    int NEG = -1000000000;
    int dp0 = nums[0];        // 未翻转
    int dp1 = -nums[0];       // 翻转中,长度=1
    int dp2 = NEG;            // 翻转中,长度≥2
    int dp3 = NEG;            // 翻转已结束
    int res = max(dp0, dp1);
    
    for (int i = 1; i < numsSize; i++) {
        int num = nums[i];
        int new_dp0 = max(num, dp0 + num);
        int new_dp1 = max(-num, dp0 - num);
        int new_dp2 = max(dp1 - num, dp2 - num);
        int new_dp3 = max3(num, dp2 + num, dp3 + num);
        
        dp0 = new_dp0;
        dp1 = new_dp1;
        dp2 = new_dp2;
        dp3 = new_dp3;
        
        res = max(res, max(max(dp0, dp1), max(dp2, dp3)));
    }
    
    return res;
}

int main() {
    int nums1[] = {1, -2, 3, -4, 5};
    int nums2[] = {-1, -2, -3};
    int nums3[] = {1, 2, 3};
    
    printf("%d\n", maxSumAfterFlip(nums1, 5));  // 9
    printf("%d\n", maxSumAfterFlip(nums2, 3));  // 6
    printf("%d\n", maxSumAfterFlip(nums3, 3));  // 6
    
    return 0;
}

运行结果

相关推荐
小π军2 小时前
最大重叠区间数量
数据结构·算法
Logic1013 小时前
C语言/数据结构字符串题解:找出DNA序列中未配对的独特序列——排序+遍历(O(nlogn))
c语言·数据结构·字符串·数组·排序·时间复杂度·算法题
Navigator_Z3 小时前
LeetCode //C - 1248. Count Number of Nice Subarrays
c语言·算法·leetcode
是隼人4 小时前
buuctf-pwn picoctf_2018_shellcode(ret2shellcode)题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门
wzdark4 小时前
基于链表的内存池设计与内存复用机制4
java·数据结构·链表
潜创微科技4 小时前
IT6520:USB-C 转 MIPI 高集成控制器,DP 1.4a 转 MIPI 单芯片搞定 4K120 显示
c语言·开发语言·低延迟·掌机·联阳
白色的北极熊5 小时前
字符转 ASCII 码
c语言
BizzZ_6 小时前
算法(1)——双指针
数据结构
Doubbbbbbble云6 小时前
内存碎片化对数据结构操作性能的影响研究4
数据结构