Hard

题目描述

给你一个整数数组 nums。数组的价值定义为所有 0 <= i < nums.length - 1 的 |nums[i] - nums[i + 1]| 的总和。

你可以选择给定数组的任何子数组并将其反转。你只能执行此操作一次。

找到最终数组的最大可能价值。

示例 1:

输入:nums = [2,3,1,5,4]
输出:10
解释:通过反转子数组 [3,1,5],数组变为 [2,5,1,3,4],其价值为 10。

示例 2:

输入:nums = [2,4,9,24,2,1,10]
输出:68

提示:

  • 2 <= nums.length <= 3 * 10^4
  • -10^5 <= nums[i] <= 10^5
  • 答案保证能够用 32 位整数表示。

解题思路

这道题的关键在于理解反转子数组对数组价值的影响。

当我们反转子数组 [L, R] 时,数组价值的变化来自于边界处的差异:

  • 原来的连接:a[L-1] 连接 a[L],a[R] 连接 a[R+1]
  • 反转后的连接:a[L-1] 连接 a[R],a[L] 连接 a[R+1]

因此,价值的变化量为: |a[L-1] - a[R]| + |a[L] - a[R+1]| - |a[L-1] - a[L]| - |a[R] - a[R+1]|

由于绝对值函数的性质,我们需要考虑四种情况来最大化这个表达式:

  1. (a[R] - a[L-1]) + (a[R+1] - a[L])
  2. (a[R] - a[L-1]) + (a[L] - a[R+1])
  3. (a[L-1] - a[R]) + (a[R+1] - a[L])
  4. (a[L-1] - a[R]) + (a[L] - a[R+1])

为了高效计算,我们可以重写这些表达式:

  • Case 1: (a[R] + a[R+1]) - (a[L-1] + a[L])
  • Case 2: (a[R] - a[R+1]) - (a[L-1] - a[L])
  • Case 3: -(a[R] - a[R+1]) + (a[L-1] - a[L])
  • Case 4: -(a[R] + a[R+1]) + (a[L-1] + a[L])

对于每种情况,我们维护前缀最大值和最小值来高效计算所有可能的 L,R 组合。

算法步骤:

  1. 计算原数组的价值
  2. 对每个可能的右边界 R,使用前缀最值来计算四种情况的最大增益
  3. 返回原价值加上最大增益

代码实现

class Solution {
public:
    int maxValueAfterReverse(vector<int>& nums) {
        int n = nums.size();
        
        // 计算原始价值
        int originalValue = 0;
        for (int i = 0; i < n - 1; i++) {
            originalValue += abs(nums[i] - nums[i + 1]);
        }
        
        int maxGain = 0;
        
        // 处理边界情况:反转从开头开始或到结尾结束的子数组
        for (int i = 1; i < n; i++) {
            // 反转 [0, i]
            maxGain = max(maxGain, abs(nums[i] - nums[i - 1]) - abs(nums[0] - nums[1]));
        }
        for (int i = 0; i < n - 1; i++) {
            // 反转 [i, n-1]
            maxGain = max(maxGain, abs(nums[i] - nums[i + 1]) - abs(nums[n - 2] - nums[n - 1]));
        }
        
        // 处理一般情况:反转中间的子数组
        for (int case_type = 0; case_type < 4; case_type++) {
            int maxPrefix = INT_MIN;
            
            for (int r = 1; r < n - 1; r++) {
                int left_val, right_val;
                
                switch (case_type) {
                    case 0:
                        left_val = nums[r - 1] + nums[r];
                        right_val = nums[r] + nums[r + 1];
                        break;
                    case 1:
                        left_val = nums[r - 1] - nums[r];
                        right_val = nums[r] - nums[r + 1];
                        break;
                    case 2:
                        left_val = -nums[r - 1] + nums[r];
                        right_val = -nums[r] + nums[r + 1];
                        break;
                    case 3:
                        left_val = -nums[r - 1] - nums[r];
                        right_val = -nums[r] - nums[r + 1];
                        break;
                }
                
                maxPrefix = max(maxPrefix, left_val);
                int gain = maxPrefix + right_val - abs(nums[r - 1] - nums[r]) - abs(nums[r] - nums[r + 1]);
                maxGain = max(maxGain, gain);
            }
        }
        
        return originalValue + maxGain;
    }
};
class Solution:
    def maxValueAfterReverse(self, nums: List[int]) -> int:
        n = len(nums)
        
        # 计算原始价值
        original_value = sum(abs(nums[i] - nums[i + 1]) for i in range(n - 1))
        
        max_gain = 0
        
        # 处理边界情况:反转从开头开始或到结尾结束的子数组
        for i in range(1, n):
            # 反转 [0, i]
            max_gain = max(max_gain, abs(nums[i] - nums[i - 1]) - abs(nums[0] - nums[1]))
        
        for i in range(n - 1):
            # 反转 [i, n-1]
            max_gain = max(max_gain, abs(nums[i] - nums[i + 1]) - abs(nums[n - 2] - nums[n - 1]))
        
        # 处理一般情况:反转中间的子数组
        for case_type in range(4):
            max_prefix = float('-inf')
            
            for r in range(1, n - 1):
                if case_type == 0:
                    left_val = nums[r - 1] + nums[r]
                    right_val = nums[r] + nums[r + 1]
                elif case_type == 1:
                    left_val = nums[r - 1] - nums[r]
                    right_val = nums[r] - nums[r + 1]
                elif case_type == 2:
                    left_val = -nums[r - 1] + nums[r]
                    right_val = -nums[r] + nums[r + 1]
                else:
                    left_val = -nums[r - 1] - nums[r]
                    right_val = -nums[r] - nums[r + 1]
                
                max_prefix = max(max_prefix, left_val)
                gain = max_prefix + right_val - abs(nums[r - 1] - nums[r]) - abs(nums[r] - nums[r + 1])
                max_gain = max(max_gain, gain)
        
        return original_value + max_gain
public class Solution {
    public int MaxValueAfterReverse(int[] nums) {
        int n = nums.Length;
        
        // 计算原始价值
        int originalValue = 0;
        for (int i = 0; i < n - 1; i++) {
            originalValue += Math.Abs(nums[i] - nums[i + 1]);
        }
        
        int maxGain = 0;
        
        // 处理边界情况:反转从开头开始或到结尾结束的子数组
        for (int i = 1; i < n; i++) {
            // 反转 [0, i]
            maxGain = Math.Max(maxGain, Math.Abs(nums[i] - nums[i - 1]) - Math.Abs(nums[0] - nums[1]));
        }
        
        for (int i = 0; i < n - 1; i++) {
            // 反转 [i, n-1]
            maxGain = Math.Max(maxGain, Math.Abs(nums[i] - nums[i + 1]) - Math.Abs(nums[n - 2] - nums[n - 1]));
        }
        
        // 处理一般情况:反转中间的子数组
        for (int caseType = 0; caseType < 4; caseType++) {
            int maxPrefix = int.MinValue;
            
            for (int r = 1; r < n - 1; r++) {
                int leftVal, rightVal;
                
                switch (caseType) {
                    case 0:
                        leftVal = nums[r - 1] + nums[r];
                        rightVal = nums[r] + nums[r + 1];
                        break;
                    case 1:
                        leftVal = nums[r - 1] - nums[r];
                        rightVal = nums[r] - nums[r + 1];
                        break;
                    case 2:
                        leftVal = -nums[r - 1] + nums[r];
                        rightVal = -nums[r] + nums[r + 1];
                        break;
                    default:
                        leftVal = -nums[r - 1] - nums[r];
                        rightVal = -nums[r] - nums[r + 1];
                        break;
                }
                
                maxPrefix = Math.Max(maxPrefix, leftVal);
                int gain = maxPrefix + rightVal - Math.Abs(nums[r - 1] - nums[r]) - Math.Abs(nums[r] - nums[r + 1]);
                maxGain = Math.Max(maxGain, gain);
            }
        }
        
        return originalValue + maxGain;
    }
}
var maxValueAfterReverse = function(nums) {
    const n = nums.length;
    
    // 计算原始价值
    let originalValue = 0;
    for (let i = 0; i < n - 1; i++) {
        originalValue += Math.abs(nums[i] - nums[i + 1]);
    }
    
    let maxGain = 0;
    
    // 处理边界情况:反转从开头开始或到结尾结束的子数组
    for (let i = 1; i < n; i++) {
        // 反转 [0, i]
        maxGain = Math.max(maxGain, Math.abs(nums[i] - nums[i - 1]) - Math.abs(nums[0] - nums[1]));
    }
    
    for (let i = 0; i < n - 1; i++) {
        // 反转 [i, n-1]
        maxGain = Math.max(maxGain, Math.abs(nums[i] - nums[i + 1]) - Math.abs(nums[n - 2] - nums[n - 1]));
    }
    
    // 处理一般情况:反转中间的子数组
    for (let caseType = 0; caseType < 4; caseType++) {
        let maxPrefix = -Infinity;
        
        for (let r = 1; r < n - 1; r++) {
            let leftVal, rightVal;
            
            switch (caseType) {
                case 0:
                    leftVal = nums[r - 1] + nums[r];
                    rightVal = nums[r] + nums[r + 1];
                    break;
                case 1:
                    leftVal = nums[r - 1] - nums[r];
                    rightVal = nums[r] - nums[r + 1];
                    break;
                case 2:
                    leftVal = -nums[r - 1] + nums[r];
                    rightVal = -nums[r] + nums[r + 1];
                    break;
                case 3:
                    leftVal = -nums[r - 1] - nums[r];
                    rightVal = -nums[r] - nums[r + 1];
                    break;
            }
            
            maxPrefix = Math.max(maxPrefix, leftVal);
            const gain = maxPrefix + rightVal - Math.abs(nums[r - 1] - nums[r]) - Math.abs(nums[r] - nums[r + 1]);
            maxGain = Math.max(maxGain, gain);
        }
    }
    
    return originalValue + maxGain;
};

复杂度分析

复杂度类型
时间复杂度O(n)
空间复杂度O(1)