问题描述:给定一个数列,其中可能有正数也可能有负数,找出其中连续的一个子数列(不允许空序列),使它们的和尽可能大。例如:给定数组int[] arr = {-2, 6, -1, 5, 4, -7, 2, 3}; 和最大为14,其对应的子序列为{6, -1, 5, 4}。
一、暴力穷举法
枚举出所有可能的序列(即所有zi'xu'lie的起始和结束坐标)
public static void main(String[] args) { int[] arr = {-2, 6, -1, 5, 4, -7, 2, 3}; int max = arr[0]; int start = 0,end = 0; for (int i = 0; i < arr.length; i++) {//i为起始坐标 for (int j = i+1; j < arr.length; j++) {//j为终止坐标 int sum = 0; for (int j2 = i; j2 <= j; j2++) {//求以i开始j结尾的子序列的和 sum += arr[j2]; } if (sum > max) { max = sum; start = i; end = j; } } } System.out.println("和最大为:"+max); System.out.println("对应子序列的开始坐标为:"+start); System.out.println("对应子序列的结束坐标为:"+end); }
二、暴力穷举法的简单优化
用
sum[i]
表示第1个到第i个数的和,那么计算第i个到第j个这个序列的和用sum[j]-sum[i]就可实现。对上面代码改写public static void main(String[] args) { int[] arr = {-2, 6, -1, 5, 4, -7, 2, 3}; int max = arr[0]; int start = 0,end = 0; int[] sum = new int[arr.length]; sum[0] = arr[0]; for (int i = 1; i < arr.length; i++) { sum[i] = sum[i-1] + arr[i]; } for (int i = 0; i < arr.length; i++) {//i+1为起始坐标,因为减掉的是前i个 for (int j = i+1; j < arr.length; j++) {//j为终止坐标 if (sum[j] - sum[i] > max) { max = sum[j] - sum[i]; start = i+1; end = j; } } } System.out.println(max); System.out.println(start); System.out.println(end); }
三、动态规划
创建一个和主序列长度相等的数组
maxWithIdxIEnd
[],maxWithIdxEndI
[i]记录以第i个元素结尾的最大子序列和,于是存在以下递推公式:
max[i] = maxWithIdxIEnd(arr[i], maxWithIdxIEnd[i-1]) + arr[i]);
则整个问题的答案是
max(maxWithIdxIEnd[N]) | m∈[1, N]
。注意:需考虑数组全为负数
public static void main(String[] args) { int[] arr = {-2, 6, -1, 5, 4, -7, 2, 3}; int[] maxWithIdxIEnd = new int[arr.length]; int[][] infoOfposition = new int[arr.length][2];//保存以第i个元素结尾的最大子序列的起始和结束坐标信息 maxWithIdxIEnd[0] = arr[0]; //初始化最优子序列和为数组的第一个元素 infoOfposition[0][0] = 0;//初始化最优子序列开始位置结束位置为0 infoOfposition[0][1] = 0;//初始化最优子序列开始位置结束位置为0 for (int i = 1; i < arr.length; i++) { maxWithIdxIEnd[i] = Math.max(maxWithIdxIEnd[i-1]+arr[i], arr[i]); //记录以第i个元素结束的最优子序列的起始坐标和结束坐标 if((maxWithIdxIEnd[i-1]+arr[i]) > arr[i]){ infoOfposition[i][0] = infoOfposition[i-1][0]; infoOfposition[i][1] = i; }else { infoOfposition[i][0] = i; infoOfposition[i][1] = i; } } int max = maxWithIdxIEnd[0], maxIdx = 0;//maxIdx记录连续子序列和最大max的maxWithIdxIEnd下标 for (int i = 1; i < maxWithIdxIEnd.length; i++) { if (maxWithIdxIEnd[i] >= max) { max = maxWithIdxIEnd[i]; maxIdx = i; } } System.out.println("max number is: "+max); System.out.println("the start positon of max sub: " + infoOfposition[maxIdx][0]); System.out.println("the end positon of max sub: " + infoOfposition[maxIdx][1]); }
参照文章:http://conw.net/archives/9/