【数据结构与算法】小于等于k的最大连续子序列和

使用Kadane算法可以在On内得到最大连续子序列和。如果要求得到和不超过k,那么该如何解决?

首先要看能不能继续使用Kadane算法。答案是不能。回顾Kadane,Kadane中使用dp[i]存最后一个元素是array[i]的最大和,然后所有dp[i]的最大值。

那么首先一个想法是,找出dp[i]中小于等于k的值中的最大值。这是不正确的,因为正常来讲,要求小于等于k的最大值,我们应该求出所有的小于等于k的最大值,再取其中的最大值,也就是最基本的子问题就必须满足小于等于k的条件,如果子问题不满足,那么子问题求出的解可能过大,这样最后就会被过滤掉,而实际上,可能去掉一个或两个,某些子问题就可以使用了。

举个例子,2,2,-1,k=0。

按照没有k的方法求出dp, dp[0] = 2, dp[1] = 4, dp[2] = 3,再从其中筛选出小于等于0的,那么没有满足的子序列。事实上,子序列(2,2)。即-1是满足的,应该返回-1.所以不能直接在没有k的问题的基础上修改。

那么有人会问,直接定义dp[i]就是以array[i]结尾的小于等于k的最大值不就解决了吗?也不行。因为,当我们计算dp[i]时,要用到dp[i - 1],如果array[i] 是负数,那么相加以后值变小,此时可能让dp[i - 1]值变大也可行。所以dp的定义还是不能满足这种需求,所以这个思路也不行。

举个例子,2, 2, -1, k = 3

如果dp[i]是以array[i]结尾的小于等于k的最大值。

那么dp[0] = 2,dp[1] = 2, dp[2]=1,但是结果是3。当计算dp[2]时,想到利用前面的,因为是-1,所以此时就算把第一个元素加入,也满足,所以不能使用dp[i-1]来得到dp[i]。


总之直接使用一维数组的kadane算法不可行。

一个直观的办法就是计算所有的子序列和,可行,不过复杂度是On2. 代码如下:

public int maxSumNoLargerThan1(int[] array, int k){
		int r = Integer.MIN_VALUE;
		for(int i = 0; i < array.length; i++){
			int sum = 0;
			for(int j = i; j < array.length; j++){
				sum += array[j];
				if(sum <= k)
					r = Math.max(r, sum);
			}
		}
		return r;
	}


不过后来在网上看到一个更吊的思路,优化了查找过程。

要求sum(i,j)即从i到j的和,可以sum(0,j) - sum(0,i-1)。所以sum(0,j) - sum(0,i-1)<=k。

这样的话,我们用一个遍历,求出sum(0,i),并且把所有的值存入set,然后每一次都求出一个下界,即sum(0,i)-k,然后在之前的结果中找大于等于这个界的最小值,如果存在,就是一个候选值,然后再把sum(0,i)也放入set。

如果不使用一定的数据结构,以上算法还是On2的。但是如果set的查找使用了特殊数据结构比如平衡二叉搜索树这种的,那么查找后继或者前驱就是logn级别了。这就是优化的一个方法。在java中,使用Treeset结构即可。所以下面就是一个nlogn的算法:

	public int maxSumNoLargerThan(int[] array, int k){
		int sum = 0, max = Integer.MIN_VALUE;
		TreeSet<Integer> set = new TreeSet<>();
		set.add(0);
		for(int i = 0; i < array.length; i++){
			sum += array[i];
			Integer min = set.ceiling(sum - k);
			if(min != null)
				max = Math.max(max, sum - min);
			set.add(sum);
		}
		return max;
	}
以上都是假定不存在返回Integer.MIN_VALUE的。




  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值