最大连续子序列的和

问题描述:给定一个数列,其中可能有正数也可能有负数,找出其中连续的一个子数列(不允许空序列),使它们的和尽可能大。例如:给定数组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/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值