Maximum Subarray

问题描述

给定一个序列(A_0)(A_1)(A_2)、...、(A_{n-1}),求(A_i+A_{i+1}+...+A_j)的最大值。

解一

暴力枚举左端点(i)和右端点(j),之后计算(A_i)(A_j)之间的和,时间复杂度(O(n^3)),很容易TLE。

#define INF 0x7FFFFFFF

int sub_sum(int a[],int n)
{
	int MAX = -INF;
	for(int i = 0;i < n;i++)
	{
		for (int j = i; j < n; j++)
 		 {
   			int temp = 0;
   			for (int k = i; k <= j; k++)
  			{
    				temp += a[k];
  			}
   			if (temp > MAX)
   			{
   				MAX = temp;
   			}
 		 }
	}
	return MAX;
}

解二

输入数据时记录前缀和,预处理(sum[i] = A[0] + ... + A[i]),因此(A_i+A_{i+1}+...+A_j=sum[j]-sum[i-1]),复杂度优化为(O(n^2))

int sub_sum(int a[],int n)
{
	int MAX = -INF;
	for(int i = 0;i < n;i++)
	{
		for(int j = i;j < n;j++}
		{
			int temp = sum[j] - sum[i - 1];
			if(temp > MAX)
				MAX = temp;
			else
				temp = 0;
		}
	}
	return MAX;
}

解三

动态规划,复杂度(O(n))
定义状态数组(dp[i]),表示以(A[i])结尾的连续序列的最大和,这样就只有两种情况:
1,该连续序列只有(A[i])这一个元素;
2,该序列有多个元素,从之前的(A[p])开始,到(A[i])结束。
对于1,最大和就是(A[i])
对于2,最大和是(dp[i - 1]+A[i]),因为(dp[i])要求以(A[i])结尾,所以即使(A[i])为负数,(dp[i])仍然等于(dp[i - 1]+A[i])
所以状态转移方程就是:

[dp[i]=max{{A[i],dp[i-1]+A[i]}} ]

边界是(dp[0]=A[0])
所以枚举(i),得到(dp)数组,求出(dp)数组最大值即可。

可以看到,每次计算(dp[i])只用到(dp[i-1]),不直接用到之前的信息,这就是状态的无后效性,只有这样,动态规划才可能得到正确结果。

int dp[5010];
dp[0] = a[0];

int sub_sum(int a[],int n)
{
	for(int i = 1;i < n;i++)
	{
		//状态转移方程
		dp[i] = max(a[i],dp[i - 1] + a[i]);
	}

	int k = 0;
	for(int i = 1;i < n;i++)
	{
		if(dp[i] > dp[k])
			k = i;
	}
	return dp[k];
}

为了避免使用(dp[])数组,可以将空间复杂度优化为(O(1))

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int allSum = INT_MIN, curSum = 0;
        
        int n = nums.size();
        for(int i = 0;i < n;i++)
        {
            curSum = max(nums[i], curSum + nums[i]);
            if(curSum > allSum)
            {
                allSum = curSum;
            }
        }
        
        return allSum;
    }
};
原文地址:https://www.cnblogs.com/EIMadrigal/p/12130801.html