您的位置:首页 > 其它

最大子序列(有疑问)

2015-09-14 16:44 246 查看
题目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
;
int []local = new int
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息