子数组最大和问题

需求:

        对于一个有正数、负数或零的整数数组a[n],找出其中和最大的子数组。

分析:

        这是典型的子数组最大和问题,下面我们由浅入深考虑其处理方法:


1.生活中很多事情,最直接的方法就是暴力处理,基于此我们给出如下处理策略。

         我们可以通过一个对数组索引的两次循环来获得每一个子数组的开始与结束索引,再利用累计求和方式处理开始与结束索引之间的数组元素就可以枚举出所有子数组的和,接下来就是筛选出和最大的子数组。显而易见,这种方法的时间复杂度是O(n^3)

 

2.对于第一种处理策略,我们可以优化开始索引start与结束索引end之间元素进行累计求和这部分,将其由O(n)优化为O(1)。为了达到这个目的,我们需要在处理数组之前遍历一遍a数组,并维护一个求和数组b[n]使得b[i]=a[0]+a[1]+...+a[i]。做完这一步之后a[start]+a[start+1]+...+a[end]就转换成了b[end]-b[start-1]。我们称这种算法为预处理算法,容易看出该处理策略的时间复杂度是O(n^2)

 

3.为了寻求更高效的算法,我们可以利用动态规划进行处理。

        首先对于子数组a[0],a[1],...,a[i-1],我们可以维护一个和数组c[n],其中c[i-1]表示以a[i-1]作为子数组最后一个元素的最大和(注意这里的最大和只是目前一定要包含a[i-1]这个元素的子数组a[k],a[k+1],..,a[i-1]的最大和)。当处理数组元素a[i]时,若c[i-1]+a[i]>=a[i],亦即c[i-1]>=0时,则以a[i]作为最后一个元素的子数组便可以将a[k],a[k+1],...,a[i-1]包含进来构成一个新的最大和子数组a[k],a[k+1],...,a[i],否则我们得出a[i]自身就是满足条件的子数组,因为前面部分不但不能帮他,反而还会把它拖下水。

        有了上述思考,我们可以得出状态转移方程

         需要注意的是我们所要求的最大和需要我们进行抽取,上述动态规划只是帮我们把子数组的最大和及其起始和终止索引逼出来,由于它需要处理完原数组才会结束,而最大和子数组可能早就已经被我们抽取,所以后续处理对我们的结果没有帮助。但是对于后续未知领域,你不遍历你就不能确保目前的结果是最终的,所以它也是必要的。

          加入动态规划处理后,此时该算法的时间复杂度降到了O(n)级别。下面给出该算法在java语言下的实现:

public class Main {
	
	public static void maxSubSum(int[] a)
	{
		if(a==null || a.length<=0)	//特殊情况处理
			return;
		int n = a.length;
		int start,end;	//记录连续子数组最大和的起始索引与终止索引
		int i;
		int temp = 0;	//临时记录最大和的起始索引
		int sum,maxSum; //sum记录临时和,maxSum记录连续子数组最大和
		sum = maxSum = a[0];
		start = end = 0;
		
		for(i=1;i<n;i++)
		{
			if(sum>=0)	//落实状态转移方程c(i)=max{c(i-1)+a[i] , a[i]}
				sum+=a[i];
			else
			{
				sum = a[i];
				temp = i;	//最大和连续子数组有可能改变,所以需要记录改变后其起始索引
			}
			if(sum > maxSum)	//最大和连续子数组改变的最终评判规则
			{
				maxSum = sum;
				start = temp;
				end = i;
			}
		}
		
		System.out.println("start="+start+" end="+end);
		System.out.println("maxSum="+maxSum);
		
	}
	
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n;
		n = scan.nextInt();
		int[] a = new int[n];
		for(int i=0;i<n;i++)
			a[i] = scan.nextInt();
		
		maxSubSum(a);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值