DP动态规划基础题(Kadane算法)

108 阅读4分钟

翻转增益的最大子数组和 - MarsCode 题目:

问题描述

给定整数数组,我们称其中连续的 0 个或多个整数为一个子数组,求翻转任一子数组后,新数组中子数组的和的最大值

输入格式

第一行输入为 N,代表数组长度

第二行输入为 N 个整数,依次为数组的每个元素

输出格式

一个整数 K,代表所有可能新数组中子数组的和的最大值

输入样例

5

1 2 3 -1 4

输出样例

10

说明

选择翻转子数组 [-1, 4] 或 [1, 2, 3, -1],新数组分别是 [1, 2, 3, 4, -1] 和 [-1, 3, 2, 1, 4],二者的子数组最大和都是 10

数据范围

50% case:1 <= N <= 100-100<= arr[i] <= 100

100% case:1 <= N <= 1e6-100<= arr[i] <= 100

思路:

问题理解

给定一个整数数组,我们可以翻转任意一个子数组(即子数组中的元素顺序颠倒),然后计算新数组中所有子数组的和的最大值。

解题思路

  1. 初始子数组和

    • 首先,计算原始数组中所有子数组的和的最大值。这可以通过Kadane算法来实现。
  2. 翻转子数组的影响

    • 翻转一个子数组会影响该子数组及其周围的子数组的和。我们需要找到一个翻转子数组的方式,使得新数组中子数组的和最大。
  3. 计算翻转后的最大子数组和

    • 对于每个可能的翻转子数组,计算翻转后的新数组中所有子数组的和的最大值。
    • 选择所有可能翻转子数组中,使得新数组中子数组和最大的那个。

关键步骤

  1. Kadane算法

    • 使用Kadane算法计算原始数组的最大子数组和。
  2. 翻转子数组的影响

    • 对于每个可能的翻转子数组,计算翻转后的新数组中所有子数组的和的最大值。
    • 这可以通过计算翻转子数组的前后部分的最大子数组和来实现。
  3. 综合考虑

    • 综合考虑原始数组的最大子数组和以及所有可能翻转子数组后的最大子数组和,选择最大的那个作为最终答案

Kadane算法是一种用于求解最大子数组和的经典算法。它的核心思想是通过动态规划来逐步计算当前子数组的和,并记录最大子数组的和。

Kadane算法的工作原理

  1. 初始化

    • 初始化两个变量:maxEndingHere 和 maxSoFar
    • maxEndingHere 表示以当前元素结尾的最大子数组和。
    • maxSoFar 表示全局的最大子数组和。
  2. 遍历数组

    • 从数组的第一个元素开始遍历,逐步更新 maxEndingHere 和 maxSoFar

    • 对于每个元素 array[i],更新 maxEndingHere 为 max(array[i], maxEndingHere + array[i])

      • 这意味着如果当前元素 array[i] 比 maxEndingHere + array[i] 大,则从当前元素开始重新计算子数组和。
    • 更新 maxSoFar 为 max(maxSoFar, maxEndingHere)

      • 这意味着记录全局的最大子数组和。
  3. 返回结果

    • 遍历结束后,maxSoFar 即为最大子数组和。
private static int kadane(int[] array) {
    int maxEndingHere = array[0];
    int maxSoFar = array[0];
    
    for (int i = 1; i < array.length; i++) {
        maxEndingHere = Math.max(array[i], maxEndingHere + array[i]);
        maxSoFar = Math.max(maxSoFar, maxEndingHere);
    }
    
    return maxSoFar;
}

通过代码:

public class Main {
    public static int solution(int N, int[] data_array) {
        // 计算原始数组的最大子数组和
        int originalMaxSum = kadane(data_array);
        
        // 计算翻转子数组后的最大子数组和
        int maxSumAfterFlip = originalMaxSum;
        
        for (int i = 0; i < N; i++) {
            for (int j = i; j < N; j++) {
                // 翻转子数组 [i, j]
                int[] flippedArray = flipSubarray(data_array, i, j);
                
                // 计算翻转后的最大子数组和
                int flippedMaxSum = kadane(flippedArray);
                
                // 更新最大值
                maxSumAfterFlip = Math.max(maxSumAfterFlip, flippedMaxSum);
            }
        }
        
        return maxSumAfterFlip;
    }
    
    // Kadane算法计算最大子数组和
    private static int kadane(int[] array) {
        int maxEndingHere = array[0];
        int maxSoFar = array[0];
        
        for (int i = 1; i < array.length; i++) {
            maxEndingHere = Math.max(array[i], maxEndingHere + array[i]);
            maxSoFar = Math.max(maxSoFar, maxEndingHere);
        }
        
        return maxSoFar;
    }
    
    // 翻转子数组 [start, end]
    private static int[] flipSubarray(int[] array, int start, int end) {
        int[] flippedArray = array.clone();
        
        while (start < end) {
            int temp = flippedArray[start];
            flippedArray[start] = flippedArray[end];
            flippedArray[end] = temp;
            start++;
            end--;
        }
        
        return flippedArray;
    }

    public static void main(String[] args) {
        // 测试用例
        int N = 4;
        int[] data_array = {-3, -1, -2, 3};
        System.out.println(solution(N, data_array)); // 预期输出: 3
    }
}