最大乘积子数组


《编程之美》上有一道关于在长度为n的数组中找到n-1个元素乘积最大的题目,不过这并不是本文要讨论的。


本文讨论的是另一种情况,给定一个长度为n的浮点数组,找一个长度任意的子数组(子数组的元素在原数组中是连续存放的),这个子数组的乘积最大。

通常,找一个满足指定条件子数组都会使用动态规划。递归缩小问题规模的同时,保持问题的数目不会指数增长。不过,本文的这个问题中,情况稍复杂

一些,由于数组元素可能为正,为负,为0,绝对值可能大于1,也可能小于1。要使用动态规划,需要合理设计递归逻辑和保存的临时数据。


定义p(m)从位置m开始,包含第m个元素的最大子数组乘积,那么我们这个问题的解就是: maxp(n) = max { p(i), 0<=i<=n} 。

而p(m)我们希望从p(m+1)计算出来,由于数组中元素可能为正,也可能为负,不妨再定义:q(m)为从位置m开始,包含第m个元素的最小数组乘积(这个乘积必须为非正数)。

那么,

p(m) = p(m+1)*a[m] ,         p(m+1)>=1且a[m]>0

             0,                              a[m]=0

             a[m],                         p(m+1)<1且a[m]>0

             q(m+1)*a[m],           a[m]<0


q(m) = q(m+1)*a[m],           a[m]>0

             0,                               a[m]=0

             a[m],                          p(m+1)<1且a[m]<0

             a[m]*p(m+1)            p(m+1)>=1且a[m]<0


p(n)和q(n)作为初始值可以很容易知道。因此整个问题只需要O(n)的时间复杂度以及O(1)的空间复杂度。我写的代码如下,如有错误欢迎指正。


/* author zpf.073@gmail.com
 * date June 29, 2013
 */
#include<iostream>

using namespace std;

float lastMaxPN=-1.;
int lastMaxPB=-1;
int lastMaxPE=-1;

float maxPN=-1.;
int maxPB=-1;
int maxPE=-1;

float lastMinNN=1.;
int lastMinNB=-1;
int lastMinNE=-1;

float minNN=1.;
int minNB=-1;
int minNE=-1;

int main() {
	int lenNums = 0, i, n, temp, tempIndex;
	float *nums;
	cin>>lenNums;
	nums = new float[lenNums];
	for (i=0; i<lenNums; i++) {
		cin>>nums[i];
	}
	n = lenNums - 1;
	if (nums[n] >= 0) { // should be carefull about the situation when nums[n]=0.
		lastMaxPN = maxPN = nums[n];
		lastMaxPB = maxPB = lastMaxPE = maxPE = n;
	} else {
		lastMinNN = minNN = nums[n];
		lastMinNB = minNB = lastMinNE = minNE = n;
	}
	n --;	
	while (n>=0) {
		if (nums[n] > 0) {
			//update lastMaxPN if possible:
			if (lastMaxPN <= 0) { 
				lastMaxPN = nums[n];
        			lastMaxPB = lastMaxPE = n;
			} else {
				if (lastMaxPN >= 1) {
					lastMaxPN *= nums[n];
					lastMaxPB = n;
				} else if (nums[n]>lastMaxPN) {
					lastMaxPN = nums[n];
					lastMaxPB = lastMaxPE = n;
				}
			}

			//update lastMinNN if possible:
			if (lastMinNN <= 0) {
				lastMinNN *= nums[n];
				lastMinNB = n;
			}
		} else if (nums[n] == 0) {
			lastMaxPN = lastMinNN = 0;
			lastMaxPB = lastMaxPE = lastMinNB = lastMinNE = n;
		} else {
			temp = lastMinNN;
			tempIndex = lastMinNE;
			if (lastMaxPN*nums[n] <= temp) {
				lastMinNN = lastMaxPN*nums[n];
				lastMinNB = n;
				lastMinNE = lastMaxPE;
			}
			if (lastMinNN > nums[n]) {
                lastMinNN = nums[n];
                lastMinNB = lastMinNE = n;
            }
			if (temp <=0) {
				if (temp*nums[n] > lastMaxPN) {
					lastMaxPN = temp*nums[n];
					lastMaxPB = n;
					lastMaxPE = tempIndex;
				}
			}
		}
		//update max positive number and min negative number:
		if (lastMaxPN > maxPN && (lastMaxPE-lastMaxPB)>(maxPE-maxPB)) {
            maxPN = lastMaxPN;
            maxPB = lastMaxPB;
            maxPE = lastMaxPE;
        }
        if (lastMinNN < minNN && (lastMinNE-lastMinNB)>(minNE-minNB)) {
            minNN = lastMinNN;
            minNB = lastMinNB;
            minNE = lastMinNE;
        }
		n --;
	}
	cout<<maxPN<<"("<<maxPB<<":"<<maxPE<<")"<<endl;

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值