最大子序列(有疑问)
2015-09-14 16:44
253 查看
题目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])
题解:
题目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])
方法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]开始)。序列的最大字段和即为这三种情形的最大值。
方法3:(这道题的贪心思想不是很理解,不知道是否能用图形说明问题)
采用滑动窗口解决。sum 如果小于0,置为0,再加上当前值。然后再与max相比,取大的。
题目3:给定一个整数数组,找出两个不重叠子数组使得它们的和最大。每个子数组的数字在数组中的位置应该是连续的。返回最大的和。
例子:给出数组[1, 3, -1, 2, -1, 2],这两个子数组分别为[1, 3]和[2, -1, 2]或者[1, 3, -1, 2]和[2],它们的最大和都是7
方法:分别计算每个点按从左至右和从右至左的最大子段和,对每个点计算两边和,求其中最大值。
题目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)
题目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为止。
例子:序列 [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; }
相关文章推荐
- mysql binlog二进制日志详解
- C++动态规划之最长公子序列实例
- C++动态规划之背包问题解决方法
- 整理C# 二进制,十进制,十六进制 互转
- c#二进制逆序方法详解
- JS幻想 读取二进制文件第1/2页
- 使用jscript实现二进制读写脚本代码
- C#二进制序列化实例分析
- PHP函数篇详解十进制、二进制、八进制和十六进制转换函数说明
- javascript 二进制运算技巧解析
- 如何判断一个整数的二进制中有多少个1
- MSSQL 将截断字符串或二进制数据问题的解决方法
- 二进制中1的个数
- C++ 十进制转换为二进制的实例代码
- C语言十进制转二进制代码实例
- C#使用动态规划解决0-1背包问题实例分析
- C++二进制翻转实例分析
- PowerShell把IP地址转换成二进制的方法
- 科学知识:二进制、八进制、十进制、十六进制转换
- C#读取二进制文件方法分析