最大子序列(有疑问)

题目1:找出一个序列中乘积最大的连续子序列(至少包含一个数)。
例子:序列 [2,3,-2,4] 中乘积最大的子序列为 [2,3] ,其乘积为6。

方法:动态规划。以A[i]结尾的max product subarray同时取决于以A[i-1]结尾的max / min product subarray以及A[i]本身。因此,对每个i,需要记录min/max product两个状态:

max_product[i] = max(max_product[i-1]*A[i], min_product[i-1]*A[i], A[i])

min_product[i] = min(max_product[i-1]*A[i], min_product[i-1]*A[i], A[i])

题解

class Solution {
public:
    int maxProduct(int A[], int n) {
        if(n<=0) return 0;
        int ret, curMax, curMin;
        ret = curMax = curMin = A[0];
        for(int i=1; i<n; i++) {
            int temp = curMax;
            curMax = max(max(curMax*A[i], curMin*A[i]),A[i]);
            curMin = min(min(temp*A[i], curMin*A[i]),A[i]);
            ret = max(ret, curMax);
        }
        return ret;
    }
};

题目2:给定一个整数数组,找到一个具有最大和的子数组,返回其最大和。
例子:给出数组[−2,2,−3,4,−1,2,1,−5,3],符合要求的子数组为[4,−1,2,1],其最大和为6
挑战:要求时间复杂度为O(n)。

方法1:动态规划,复杂度O(n)。在每一步,我们维护两个变量,一个是全局最优,就是到当前元素为止最优的解是,一个是局部最优,就是必须包含当前元素的最优的解。

local[i+1]=Math.max(A[i], local[i]+A[i])

global[i+1]=Math(local[i+1],global[i])

public class Solution {
    /**
     * @param nums: a list of integers
     * @return: A integer indicate the sum of minimum subarray
     */
    public int maxSubArray(ArrayList<Integer> nums) {
        // write your code
        if(nums.size()==0)  
            return 0;  
        int n = nums.size();
        int []global = new int[n];
        int []local = new int[n];
        global[0] = nums.get(0);
        local[0] = nums.get(0);
        for(int i=1;i<n;i++)  
        {  
            local[i] = Math.max(nums.get(i),local[i-1]+nums.get(i));  
            global[i] = Math.max(local[i],global[i-1]);  
        }  
        return global[n-1];  
    }
}

方法2:分治法,复杂度O(nlogn)。
如果将给定的序列a[1..n]分成长度相等的两段a[1..n/2]和a[n/2+1:n],分别求出这两段的最大字段和。则该给定序列的最大字段和有三种情行:

1)和a[1..n/2]的最大字段和相同。

2)和a[n/2+1:n]的最大字段和相同。

3)最大字段和包含两部分,一部分在中,另一部分在a[n/2+1..n]中。

前两种情形我们可以用递归方法求出,第三种情形可以分别求出两部分的最大字段和值再相加(注:a[1..n/2]这部分求最大字段和要以a[n/2]结束,a[n/2+1..n] 这部分求最大字段和要以a[n/2+1]开始)。序列的最大字段和即为这三种情形的最大值。

class Solution {
public:
    int maxSubArray(int A[], int n) {
        //最大字段和问题
        return helper(A, 0, n-1);
    }
private:
    int helper(int A[], const int istart, const int iend)
    {
        if(istart == iend)return A[iend];
        int middle = (istart + iend) / 2;
        int maxLeft = helper(A, istart, middle);
        int maxRight = helper(A, middle + 1, iend);
        int midLeft = A[middle];
        int tmp = midLeft;
        for(int i = middle - 1; i >= istart; i--)
        {
            tmp += A[i];
            if(midLeft < tmp)midLeft = tmp;
        }
        int midRight = A[middle + 1];
        tmp = midRight;
        for(int i = middle + 2; i <= iend; i++)
        {
            tmp += A[i];
            if(midRight < tmp)midRight = tmp;
        }
        return max(max(maxLeft, maxRight), midLeft + midRight);
    }
};

方法3:(这道题的贪心思想不是很理解,不知道是否能用图形说明问题)
采用滑动窗口解决。sum 如果小于0,置为0,再加上当前值。然后再与max相比,取大的。

public class Solution {
    public int maxSubArray(int[] A) {
        if (A == null || A.length == 0) {
            return 0;
        }

        int max = Integer.MIN_VALUE;
        int sum = 0;

        int len = A.length;
        for (int i = 0; i < len; i++) {
            if (sum < 0) {
                sum = 0;
            }

            sum += A[i];
            max = Math.max(max, sum);
        }

        return max;
    }
}

题目3:给定一个整数数组,找出两个不重叠子数组使得它们的和最大。每个子数组的数字在数组中的位置应该是连续的。返回最大的和。
例子:给出数组[1, 3, -1, 2, -1, 2],这两个子数组分别为[1, 3]和[2, -1, 2]或者[1, 3, -1, 2]和[2],它们的最大和都是7

方法:分别计算每个点按从左至右和从右至左的最大子段和,对每个点计算两边和,求其中最大值。

public class Solution {
    /**
     * @param nums: A list of integers
     * @return: An integer denotes the sum of max two non-overlapping subarrays
     */
    public int maxTwoSubArrays(ArrayList<Integer> nums) {
        if (nums.size()<2) return 0;
        int len = nums.size();

        //Calculate the max subarray from left to right and from right to left.
        int[] left = new int[len];
        left[0] = nums.get(0);
        for (int i=1;i<len;i++){
            left[i] = Math.max(left[i-1]+nums.get(i), nums.get(i));
        }
        int curMax = left[0];
        for (int i=1;i<len;i++){
            if (left[i]<curMax){
                left[i] = curMax;
            } 
            else {
                curMax = left[i];
            }
        }

        int[] right = new int[len];
        right[len-1]=nums.get(len-1);
        for (int i=len-2;i>=0;i--){
            right[i] = Math.max(right[i+1]+nums.get(i),nums.get(i));
        }
        curMax = right[len-1];
        for (int i=len-2;i>=0;i--){
            if (right[i]<curMax) {
                right[i] = curMax;
            }
            else {
                curMax = right[i];
            }
        }
        //Find out the result.
        int res = Integer.MIN_VALUE;
        for (int i=0;i<len-1;i++){
            if (left[i]+right[i+1]>res){
                res = left[i]+right[i+1];
            }
        }
        return res;
    }
}

题目4:给定一个整数数组和一个整数k,找出k个不重叠子数组使得它们的和最大。每个子数组的数字在数组中的位置应该是连续的。返回最大的和。
例子:给出数组[-1,4,-2,3,-2,3]以及k=2,返回 8
挑战:要求时间复杂度为O(n)。

方法:动态规划。d[i][j]表示从前i个元素中选择j个子段所能达到的最大和。

d[i][j] = max{d[p][j-1]+maxSubArray(p+1,i)} (i - 1 <= p <= j - 1)

public class Solution {
    /**
     * @param nums: A list of integers
     * @param k: An integer denote to find k non-overlapping subarrays
     * @return: An integer denote the sum of max k non-overlapping subarrays
     */
    public int maxSubArray(ArrayList<Integer> nums, int k) {
        if (nums.size()<k) return 0;
        int len = nums.size();
        //d[i][j]: select j subarrays from the first i elements, the max sum we can get.
        int[][] d = new int[len+1][k+1];
        for (int i=0;i<=len;i++) d[i][0] = 0;        

        for (int j=1;j<=k;j++)
            for (int i=j;i<=len;i++){
                d[i][j] = Integer.MIN_VALUE;
                //Initial value of endMax and max should be taken care very very carefully.
                int endMax = 0;
                int max = Integer.MIN_VALUE;                
                for (int p=i-1;p>=j-1;p--){
                    endMax = Math.max(nums.get(p), endMax+nums.get(p));
                    max = Math.max(endMax,max);
                    if (d[i][j]<d[p][j-1]+max)
                        d[i][j] = d[p][j-1]+max;                    
                }
            }

        return d[len][k];
    }
}

题目5:求绝对值最大子序列和以及对应的区间(不会做,画图方法)

题目6:有一个包含n个元素的首尾相连的环形数组arr,计算最大的子段和。
例子:数组[1, 3, -2, 6, -1],最大子段和应该为9,对应的子段为[6, -1, 1, 3]。

题目7:输出最大子段和在原序列中的起始位置
方法:用一个数组 f[]来记录哪些元素被选入,f[i] = true表示dp[i] 的计算结果中使用了dp[i-1],这样我们可以知道,只要这样递推下去,那么最大子段和的那部分所有f[i] = true,我们只需要记录最大子段和的最后一个元素的位置,然后往前推,到第一个为f[i] = false为止。

int Work(int a[],int n)
{
    L = R = 0;
    dp[0] = a[0];
    f[0] = false;
    int ans = a[0];
    for(int i=1; i<n; i++)
    {
        if(dp[i-1] < 0)
        {
            f[i] = false;
            dp[i] = a[i];
        }
        else
        {
            f[i] = true;
            dp[i] = dp[i-1] + a[i];
        }
        if(ans < dp[i])
        {
            ans = dp[i];
            R = i;
        }
    }
    for(int i=R; i>=0; i--)
    {
        if(!f[i])
        {
            L = i;
            break;
        }
    }
    L++; R++;
    return ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值