最大子序列(有疑问)

来源:互联网 发布:mac图片浏览器 编辑:程序博客网 时间:2024/04/28 19:40

题目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;}
0 0
原创粉丝点击