求解最大连续子序列的和以及最大连续子序列


前言

使用蛮力法、分治法、动态规划法求解最大连续子序列的和以及最大连续子序列


`注意:以下是本人对该问题的理解以及解题思路,如有错误地方,欢迎大家指出

一、蛮力法求解

1.算法基本思想

蛮力法是一种简单直接地解决问题的方法,常常直接基于问题的描述进行求解

2.算法设计

第一步:初始化变量max用来存储最大值,变量left和right用于存储最大字段和的起始和终止下标
第二步:for循环i从第1个数到第N个数:
初始化变量b用于存储从i到内层for循环j的的和
for循环j从第i个数到第N个数:
b+=a[j];
判断b和max的值进行取舍如果b>max说明当前找到最大值并且当前最大子序列从i到j,更新left和right指向
第三步:当双层for循环结束后max中存储的是最大连续字段的和,left和righ分
别指向最大字段和的起始和结束位置

代码如下:

import java.util.Scanner;
public class Main1 {
public static void main(String[] args) {
	Scanner sc=new Scanner(System.in);
	int c=sc.nextInt();
	int a[]=new int[c];
	int sum[]=new int[c];//用于值记录
	for(int i=0;i<c;i++) {
		a[i]=sc.nextInt();
	}
	int max=0,start=0,end=0;
	for(int i=0;i<c;i++) {//用于最大字段的开始位置
		int b=0;
		for(int j=i;j<c;j++) {//用于找出以i开头的最大字段
			b=b+a[j];//用于存储从i到j的的和
			if(max<b) {
				max=b;//更新最大字段和的值
				start=i;end=j;//更新起始和终止下标
			}
		}
	}
	System.out.println(max);
	for(int i=start;i<=end;i++) {
		System.out.print(a[i]+" ");
	}
}
}


二、分治法求解

1.算法基本思想

将一个难以直接解决的大问题划分为一些规模较小的子问题,分别求解各个子问题,再合并子问题的解得到原问题的解

2.算法设计

第一步划分:按照平衡子问题原理将序列划分成两个长度相同的两个子序列。问题结果出现三种情况:1.问题解在左边的划分区间里2.问题解在右边的划分区间里3.问题解同时在左右划分区间里

第二步求解子问题:对于划分阶段的情况1和2可递归求解,3需要分别计算从划分位置开始到左边的最大字段和的值和从划分位置开始到右边最大字段和的值的和

第三步合并:比较划分阶段的三种情况下的最大字段和,由于程序需要输出最大字段和的值,所以需要更新每一阶段的最大字段和的值以及它的开始和结束位置,直到递归结束返回最大字段和的值以及开始和结束下标
求最大连续字段的设计思想:当递归到最后序列为1时,返还当前最大字段和的值的同时应当返还当前值下标作为开始和结束位置(为了规避当程序的解只在化分的左区间或右区间时,无法得到相信值的下标),递归合并阶段时当寻找并更新最大连续字段和的值时应当同时更新它的开始和结束位置作为返还,直到递归结束,得到最大连续字段和的值以及最大连续字段的开始和结束位置。

代码如下:

import java.util.Scanner;
public class Main2 {
public static int Maxsum(int a[],int left,int right,int b[]) {
	int sum=0;
	int leftsum=0,rightsum=0;
	if(left==right) {//划分为一个元素时直接求解
		b[0]=left;b[1]=left;
        return a[left];
	}else {
		int bl[]=new int[2];//暂存左边的最大子序列起始和结束位置
		int br[]=new int[2];//暂存右边的最大子序列起始和结束位置
		int mid[]=new int[2];//暂存中间的最大子序列起始和结束位置
		int center=(left+right)/2;//划分左右两边尽可能相等
		leftsum=Maxsum(a,left,center,bl);//调用自身左边不断划分
		rightsum=Maxsum(a,center+1,right,br);//调用自身右边不断划分
		int s1=0,lf=0;
		for(int i=center;i>=left;i--) {//找到从划分位置开始左边最大字段和
			lf+=a[i];
			if(lf>s1) {
				s1=lf;
				mid[0]=i;
			}
		}
		int s2=0,rgt=0;
		for(int i=center+1;i<=right;i++) {//找到从划分位置开始右边最大字段和
			rgt+=a[i];
			if(rgt>s2) {
				s2=rgt;
				mid[1]=i;
			}
		}
		int midsum=s1+s2;
//		通过if判断当前最大字段和的值和序列的起始和结束位置
		if(leftsum>=midsum) {// leftsum>=midsum时让存储左边子序列位置,因为该程序中间的mid数组初始化为0
			sum=leftsum;
			b[0]=bl[0];b[1]=bl[1];//存储左边的最大子序列起始和结束下标
		}else {
			sum=midsum;
			b[0]=mid[0];b[1]=mid[1];//存储中间的最大子序列起始和结束下标
		}
		if(rightsum>=sum) {// rightsum>=midsum时让存储左边子序列位置,因为该程序中间的mid数组初始化为0 
			sum=rightsum;
			b[0]=br[0];b[1]=br[1];//存储右边的最大子序列起始和结束下标
		}
	}
	return sum;
}
public static void main(String[] args) {
	Scanner sc=new Scanner(System.in);
	int c=sc.nextInt();
	int a[]=new int[c];
	int m[]=new int[2];
	for(int i=0;i<c;i++) {
		a[i]=sc.nextInt();
	}
	int max=Maxsum(a,0,c-1,m);
	System.out.println(max);
	for(int i=m[0];i<=m[1];i++) {
		System.out.print(a[i]+" ");
	}
}
}

(注意:合并时只有midsum大于leftsum或rightsum时才采用mid数组的开始和结束下标,因为初始化并未给数组mid赋值,当midsum大时说明最大连续字段和的开始和结束下标一定在左右两边,这个时候数组mid的值才准确)

三、动态规划法求解

1.算法基本思想

将待求解的问题分解成若干个相互联系的子问题,先求解子问题,然后从这些子问题的解得到原问题的解;对于重复出现的子问题,只在第一次遇到的时候对它进行求解,并把答案保存起来,让以后再次遇到时直接引用答案,不必重新求解。

2.算法设计

第一步:初始化sum[]全为0,m初始化指向第一个数,即m=0, n初始化指向第一个数,即n=0
第二步:让sum[0]=a[0],m,n等于指向第一个数也就是等于0
for循环i从1取到n-1:
内部初始化变量b用于存储当前值与前一个值的最大子序列值的和
b与a[i]比较,如果b大:就将b认为当前阶段最大子序列的值,如果b小于:就认为当前阶段最大值为a[i],说明当 前值优于前面最大子序列的值,并且更新m的值等于i
n的值是每次找到最大sum值的下标,因为最大子序列的结束下标sum值一定大于或等于起始下标m的sum值
第三步:循环结束后max的值即是最大字段和m,n是最大连续字段和的开始
和结束下标

代码如下:

import java.util.Scanner;
public class Main3 {
public static void main(String[] args) {
	Scanner sc=new Scanner(System.in);
	int c=sc.nextInt();
	int a[]=new int[c];
	int sum[]=new int[c];//用于值记录
	for(int i=0;i<c;i++) {
		a[i]=sc.nextInt();
	}
	sum[0]=a[0];
	int m=0,n=0,max=a[0];//初始坐标定位第一个元素,最大值默认为第一个数
	for(int i=1;i<c;i++) {//依次计算出sum的值
		int b=a[i]+sum[i-1];
		if(a[i]>b) {
			sum[i]=a[i];
			m=i;//重新定义起始坐标
		}else {
			sum[i]=b;
		}
		if(max<sum[i]) {//记录最大值
			max=sum[i];
			n=i;
		}
	}
    if(max<0) {
    	System.out.println(0);
    }else {
    	System.out.println(max);
    	for(int i=m;i<=n;i++) {
    		System.out.print(a[i]+" ");
    	}
    }
    
}
}



以上就是今天想要跟大家分享的内容,本文代码还有可提升地方,大家可自行优化提升,欢迎大家指导。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值