当前位置:首页 > 算法 > 正文内容

【算法】最大子段和

亿万年的星光4年前 (2021-06-05)算法1816

【题目描述】

给出一个长度位n的序列a,选出其中连续且非空的一段使得这段和最大

【输入描述】

第一行是一个整数,表示序列的长度n。

第二行有n个整数,第i个整数表示序列的第i个数字ai

【输出描述】

输出一行一个整数表示答案。

【样例输入】

7
2 -4 3 -1 2 -4 3

【样例输出】

4

【题目分析】

  1. 比较经典的题目,可以用暴力求解、分治法求解、动态规划求解





【最大子段和——暴力求解】

算法描述:

int MaxSum(int n,int *a, int& besti,int& bestj) {
	int sum=0;
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++) {
			int thissum=0;
			for(int k=i; k<=j; k++)
				thissum+=a[k];
			if(thissum>sum) {
				sum=thissum;
				besti=i;
				bestj=j;
			}
		}
	return sum;
}

从上面这个算法的三个for循环可以看出,它所需的计算时间是O(n3)。事实上,可将算法中的最后一个for循环省去掉,避免重复计算,从而使算法得以改进。

int MaxSum(int n,int *a, int& besti,int& bestj) {
	int sum=0;
	for(int i=1; i<=n; i++)
		int thissum=0;
		for(int j=i;j<=n;j++){
			thissum+=a[j];
			if(thissum>sum){
				sum=thissum;
				besti=i;
				bestj=j;
			}
		}	
	return sum;
}

过程图解:

当i=1时

当i=2时


依次类推。。

改进后的算法显然只需要O(n2)的计算时间。


【最大子段和——分治算法】

如果将所给的序列a[1 : n]分为长度相等的两端a[1 : n/2]和a[n/2+1 : n], 分别求出这两段的最大子段和,则a[1 : n]的最大字段和有三种情形。

(1)a[1 : n] 的最大子段和 与 a[1 : n/2 ]的最大子段和相同;

(2)a[1 : n]的最大子段和 与 a[ n/2 +1 : n] 的最大子段和相同;

(3)a[1 : n]的最大子段和 为sum(ai~j)。且1<=i<=n/2;  n/2+1 <= j <=n;


其中,(1)和(2)这两种情形可以通过递归求得。对于情形3,容易看出,a[n/2]与a[n/2+1]在最优子序列中。因此,可以在a[1 : n/2]中计算出 左段最大值s1和右端最大值s2。则s1+s2即为出现情形3时的最优值。

int MaxSum(int* a, int beg, int end){
    if (beg == end){
        return a[beg] > 0? a[beg] :0;
    }
    int mid = (beg + end) / 2;
    int max_left = MaxSum(a, beg, mid);
    int max_right = MaxSum(a, mid + 1 ,end);
    int s1 = 0, s2 = 0, m_left = 0, m_right = 0;
    for(int i = mid; i <= beg; i --){
        s1 += a[i];
        if(s1 > m_left)
            m_left = s1;
    }
    for(int i = mid+1; i <= end; i ++){
        s2 += a[i];
        if(s2 > m_right)
            m_right = s2;
    }
    int max_sum = max_left;
    if(max_right > max_sum)
        max_sum = max_right;
    if(m_right + m_left > max_sum)
        max_sum = m_left + m_right;
    return max_sum;
}

时间复杂度O(nlogn)


【最大子段和——动态规划】

给定n个整数(可能为负数)组成的序列a1,a2,…,an。求该序列形如下式的子段和的最大值:

当所有整数均为负整数时定义其最大子段和为0。 依次定义,所求的最优值为:

例如: (a1,a2,a3,a4,a5,a6)=(-2,11,-4,13,-5,-2) 该序列的最大子段和为:

通过对分治算法的分析可知,若记:

(b[j]表示从1到j的最大子段和)

则所求的最大子段和为:

(上面的公式是由分治思想得出的)


由b[j]的定义可知: 当[公式]时:

否则:

a[j]是某个具体值,b[j]是某一段的和(如果你学过前缀和的话,这个地方就很好理解了)

输入数据:

7
2 -4 3 -1 2 -4 3
下标j0123456
a[j]2-43-12-43
说明
b[j-1]>0b[j-1]<=0,取a[i]b[j-1]>0b[j-1]>0b[j-1]>0b[j-1]<=0,取a[i]
b[j]2-232403









(是考虑到分治算法的第三种情况,可以参考下面的图解):



由此可得b[j]的动态规划递归式:




递推表达式:

                                                    dp[i] = max{dp[i-1] + A[i], A[i]}


int max = 0;
for(int i = 1; i <= n; i ++){
   if(dp[i-1] > 0){
        dp[i] = dp[i-1] + A[i];
   }else{
        dp[i] = A[i];
   }
   if(dp[i]> max){
        max = dp[i];
   }
}


递归解法:

int MaxSum(int n, int *a) {
	int sum=0,b=0;
	for(int i=1;i<=n;i++){
		if(b>0) 
			b+=a[i];
		else
			b=a[i];
		if(b>sum)
			sum=b;
	}
	return sum;
}


扫描二维码推送至手机访问。

版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。

标签: 动态规划
分享给朋友:

相关文章

【贪心】区间调度

【贪心】区间调度

【题目描述】有n项工作,每项工作分别在si时间开始,在ti时间结束。对于每项工作,你都可以选择参与与否。如果选择了参与,那么自始至终都必须全程参与。此外,参与工作的时间段不能重叠(即使是开始与结束的瞬...

【算法】最短路径算法——Floyed-Warshell算法

【算法】最短路径算法——Floyed-Warshell算法

如下图所示,我们把边带有权值的图称为带权图。边的权值可以理解为两点之间的距离。一张图中任意两点间会有不同的路径相怜。最短路径就是指连接两点的这些路径中最短的一条。【注意】边的权值可以为负。当出现负边权...

【排序】----插入排序

【排序】----插入排序

1.基本思想在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。2.过程·(1)从第一个元素开始,该元素可以...

【贪心】区间覆盖

【贪心】区间覆盖

【题目描述】给定一个长度为m的区间,再给出n条线段的起点和终点(本题考虑闭区间),求最少使用多少线段可以将整个区间完全覆盖。【输入】第一行是区间长度m。第二行是n,表示有n个可选区间。后面跟着n行数据...

【算法】高精度(1)

一、  什么是高精度高精度算法,属于处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算...

【算法】归并排序

【算法】归并排序

【参考代码】void msort(int s, int t){ if(s==t) return ;  //如果只有一...