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

【算法】最大子段和

亿万年的星光3年前 (2021-06-05)算法1356

【题目描述】

给出一个长度位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]是某一段的和


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



由此可得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;
}


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

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

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

相关文章

【贪心】区间覆盖

【贪心】区间覆盖

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

【贪心】----排队打水

【贪心】----排队打水

一、基础版排队打水【题目描述】学校里有一个水房,水房里一共装有m 个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为1。现在有n 名同学准备接水,他们的初始接水顺序已经确定。...

【算法】动态规划(二)——数字三角形问题

【算法】动态规划(二)——数字三角形问题

1.问题描述及状态定义数字三角形问题:有一个非负整数组成的三角形,第一行只有一个数,除了最下行之外每个数字的坐下方和右下方各有一个数。如下图所示:从第一行开的数开始走,每次可以往下或右走一格,直到走到...

【贪心】----基本概念

一、基本概念所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,它所做出的仅仅是在某种意义上的局部最优解。贪心算法没有固定的算法框架,算法设计的关键是贪...

【算法】最小重量机器设计

【题目描述】设某一机器由n个部件组成,每一种部件都可以从m个不同的供应商处购得。设Wij 是 从供应商j处购得的部件i的重量,Cij 是相应的价格。 试设计一个算法,给出总价格不超...

【算法】动态规划(一)

1.基本概念在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果。因此各个阶段决策的选取不能任意确定,它依赖...