问题描述
给定由 n 个整数(可能为负整数)组成的序列 a1、a2、……an,求该序列形如
的子段和的最大值。当所有整数均为负整数时定义其最大子段和为 0。依次定义,所求的最优值为
max{
例如:当(a1,a2,a3,a4,a5,a6)=(-2,11,-4,13,-5,-2)时,最大子段和为
=20。
三种算法求解
1、暴力算法
算法分析:
通过阅读了解问题后,可以初步尝试用最简单的方法解决问题,方便发现问题本质。最大子段和的结果很明显是通过:对数组排列组合得出子段和的个数,然后一一对比得出结果。
求子段和的个数需要两个循环:外循环是循环起点,内循环是循环终点。
求子段和对比结果需要一个循环。
三个for循环的时间复杂度是O(n3),然而在第二个for循环求处子段个数时就可以进行子段和比较了,省去最后一个for循环,使得算法时间复杂度为O(n2)。
算法实现:
算法伪代码:
2. 分治算法
算法分析:
可以从数组中间分开,结果要么在左边或右边,要么就在中间。结果在左边和右边的情况好办,那在中间的情况该如何分析呢?因为子段是连续的,所以结果只能从中心向两边扩,采用枚举法,左右走到尽头,结果相加就得到中间的最大解。所以该题采用分治算法的难度就可以解决了。
拆分子数组分别求得的最大子段和为sum1,sum2。时间复杂度为2T(N/2),从中心点向两边找最大的和,找到跨越的最大子段和的时间复杂度为O(n), 所以总体的时间复杂度为:T(n)=2T(n/2)+O(n) = O(nlogn)。
算法实现:
算法伪代码:
3. 动态规划
算法分析:
首先,该问题的解法符合动态规划的思想。
一、子问题重叠:无论问题规模怎么变化,最大子段和的子段都是在数组中。
二、原问题的最优解包含子问题的最优解:通过最简单的方法,我称之为暴力带值。列出小问题规模的结果,通过矩阵a[i]存入数值:
a[i] | -2 | 11 | -4 | 13 | -5 | -2 |
---|---|---|---|---|---|---|
在分治算法中,需要枚举n个数组,那如果从a[0]开始枚举,得到一段子段和是负数,那还需要用到该子段吗?显然不需要,直接从当前元素重新开始。
假定子段终点为a[j],那么之前的和必定为正且最大,设置b[i]数组记录a[j]结尾的全部子段的和,所以当b[j]>0时,b[j]=b[j-1]+a[j],否则b[j]=a[j],由此得到b[j]的动态规划递归式:
b[j]=max{b[j-1]+a[j],a[j]}, 1≤j≤n
b | -2 | 11 | 7 | 20 | 15 | 13 |
---|---|---|---|---|---|---|
sum | 0 | 11 | 7 | 20 | 20 | 20 |
算法实现:
算法伪代码:
上述算法显然时间复杂度时O(n),空间复杂度是O(n)。
算法结果
输出结果(取一次)
复杂度分析
三种算法在不同规模下运行时间如下表:
纵坐标单位为(ms),横坐标单位为(个)。
当问题规模到15000时,暴力算法明显变得笨重;当问题规=规模到达10000000时,分治算法也开始劣于动态规划。
三者的时间复杂度比较:O(n2)>O(nlogn)>O(n)。
源代码
暴力算法:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.Scanner;
public class ziduanhe1 {
public static int []a = new int[1000000];
public static void test(int n){
random_get(n);
int ThisSum,MaxSum=0;
for (int i = 0; i < n; i++) {
ThisSum = 0;
for (int j = i; j < n; j++) {
ThisSum+=a[j];
if (ThisSum>MaxSum){
MaxSum = ThisSum;
}
}
}
System.out.print(MaxSum);
}
public static int random_get(int l){
Random r = new Random();
int c = 40;
for (int i = 0; i < l; i++) {
a[i]= r.nextInt(c)%(2*c+1)-c/2;
}
return 0;
}
public static void main(String[] args){
int n;
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
n = sc.nextInt();
// for (int i = 0; i < n; i++) {
// a[i] = sc.nextInt();
// }
// ziduanhe1 t = new ziduanhe1();
long startTime = System.currentTimeMillis();
SimpleDateFormat df = new SimpleDateFormat("\n" + "yyyy-MM-dd HH:mm:ss");
System.out.println(df.format(new Date()));
test(n);
long endTime = System.currentTimeMillis();
System.out.println(df.format(new Date()));
System.out.println("\n" + "执行时间" + (endTime - startTime) + "ms");
}
}
}
分治算法:
import java.text.SimpleDateFormat;
import java.util.*;
public class ziduanhe2
{
public static int []a =new int[101000000];
public static int maxsum(int l,int r){
if(l==r){
return a[l];
}
int mid = (l+r)/2;
int lsum = maxsum(l,mid);//左区间
int rsum = maxsum(mid+1,r);//右区间
int sum1=0,sum2=0;
int lefts=0,rights=0;
for(int i=mid;i>=l;i--){
lefts+=a[i];
if(lefts>sum1){
sum1=lefts;
}
}
for(int i=mid+1;i<=r;i++){
rights+=a[i];
if(rights>sum2){
sum2=rights;
}
}
int msum=sum1+sum2;
return Math.max(Math.max(lsum,rsum),msum);
}
public static int random_get(int l){
Random r = new Random();
int c = 40;
for (int i = 0; i < l; i++) {
a[i]= r.nextInt(c)%(2*c+1)-c/2;
}
return 0;
}
public static void main(String[] args){
int n;
int ans;
Scanner in=new Scanner(System.in);
while(in.hasNext()){
n=in.nextInt();
// for(int i=1;i<=n;i++){
// a[i]=in.nextInt();
// }
random_get(n);
long startTime = System.currentTimeMillis();
SimpleDateFormat df = new SimpleDateFormat("\n"+"yyyy-MM-dd HH:mm:ss");
System.out.println(df.format(new Date()));
ans = maxsum(0,n);
if(ans<0){
ans=0;
}
System.out.println(ans);
long endTime = System.currentTimeMillis();
System.out.println(df.format(new Date()));
System.out.println("\n"+"执行时间"+(endTime-startTime)+"ms");
}
}
}
动态规划:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.Scanner;
public class ziduanhe3 {
public static int []a = new int[101000000];
public static int maxSum(int n){
random_get(n);
// for (int i = 0; i < n; i++) {
// System.out.print(a[i]);
// }
int sum = 0;
int b = 0;
for (int i = 0; i < n; i++) {
if (b>0) b+=a[i];
else b = a[i];
if (sum<b) sum = b;
}
return sum;
}
public static int random_get(int l){
Random r = new Random();
int c = 40;
for (int i = 0; i < l; i++) {
a[i]= r.nextInt(c)%(2*c+1)-c/2;
}
return 0;
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n;
int ans = 0;
System.out.print("请输入元素个数:");
while (sc.hasNext()){
n =sc.nextInt();
// for (int i = 0; i < n; i++) {
// a[i] = sc.nextInt();
// }
long startTime = System.currentTimeMillis();
SimpleDateFormat df = new SimpleDateFormat("\n"+"yyyy-MM-dd HH:mm:ss");
System.out.println(df.format(new Date()));
ans = maxSum(n);
if (ans<0){
ans = 0;
}
System.out.print("最大子段和:");
System.out.print(ans);
long endTime = System.currentTimeMillis();
System.out.println(df.format(new Date()));
System.out.println("\n"+"执行时间"+(endTime-startTime)+"ms");
}
}
}