P1115 最大子段和分治

P1115 最大子段和

给出一个长度为 nn 的序列 aa,选出其中连续且非空的一段使得这段和最大。

7
2 -4 3 -1 2 -4 3
分治的解法。

首先,假定有区间[l..r][l..r],其中间位置为midmid,其最大子段为[i..j][i..j]。那么显然,ii和jj必定符合下列三种情况之一:

1.l \leq{i} \leq{j} \leq{mid} l≤i≤j≤mid

2.i \leq{mid} <j \leq{r} i≤mid<j≤r

3.mid < i \leq{j} \leq{r} mid<i≤j≤r

只需要分别求出三种情况下的值,取其最大的即可。

其中,很容易求出第二种情况,即求出区间[i..mid][i..mid]与区间[mid+1..j][mid+1..j],将其相加即可。复杂度O(n)O(n)

如何处理第一种和第三种情况呢?也不难发现,第一种情况,其实就是求区间[1..mid][1..mid]中的最大值,第三种情况就是求区间[mid+1..r][mid+1..r]中的最大值。那么,只需递归求出即可。

显然,该解法的复杂度为 O(nlogn)O(nlogn) 通过此题是没问题的。

附上代码

`#include<cstdio>
int n , arr[200200]; //arr存储该序列 
const int minn = -19260817; // 定义最小值 
inline int Max( int a , int b) { return a > b ? a : b ;} //自定义 Max 函数(好像比stl的快一点) 
int rec( int l , int r ) { //分治函数 
    if ( l == r ) {    //    l=r时,直接返回该位置的值 
        return arr[l];
    }
    int mid = ( l + r ) >> 1;  
    int sum = 0 , ret1 = minn , ret2 = minn; //ret1为[l..mid]区间内包含mid的最大子段和,ret2为[mid+1..r]区间内包含(mid+1)的最大子段和  
    for( int i = mid ; i >= l ; i-- ) {
        sum += arr[i];
        ret1 = Max( ret1 , sum );
    }  //求出[i..mid]区间最大值
    sum = 0;
    for( int i = mid+1 ; i <= r ; i++ ) {
        sum += arr[i];
        ret2 = Max( ret2 , sum );
    }  //求出[mid+1..r]区间最大值
    return Max( Max( rec( l , mid ) , rec( mid + 1 , r ) ) , ret1 + ret2 );   //返回可能一 可能二 可能三 中的最大值
}
int main() { // 以下主函数  
    scanf("%d", &n );
    for( int i = 1 ; i <= n ; i++ ) {
        scanf("%d" , &arr[i] );
    }
    printf("%d" , rec(1 , n) ); 
    return 0;
}`
//最大子段和问题 p64 
 
#include<stdio.h>
#include<iostream>
using namespace std;
int MaxSum(int a[],int left,int right);
int a[100];
int main(void)
{
	int n,j;
	scanf("%d",&n);  
	for(j=0;j<n;j++)
	scanf("%d",&a[j]);	
	printf("%d",MaxSum(a,0,n-1));
	return 0;	
}
 
int MaxSum(int a[],int left,int right)
{
	int sum=0,midSum=0,leftSum=0,rightSum=0;
	int center,s1,s2,lefts,rights;
	if(left==right)
		sum=a[left];
	else{
		center=(left+right)/2;
		leftSum=MaxSum(a,left,center);   //情况1,最大字段和全部取左边元素 
		rightSum=MaxSum(a,center+1,right);//情况2,最大字段和全部取右边元素 
		s1=0;lefts=0;                      //情况3  最大子段和横跨中间 
		for(int i=center;i>=left;i--)      //求出s1:从中间到左边的最大和 
		{
			lefts+=a[i];
			if(lefts>s1) s1=lefts;
		}
		s2=0;rights=0;
		for(int j=center+1;j<=right;j++)    //求出s2 从中间到右边的最大和 
		{
			rights+=a[j];
			if(rights>s2) s2=rights;
		}
		midSum=s1+s2;              //横跨中间的最大字段和为s1+s2 
		if(midSum<leftSum) sum=leftSum;
		else sum=midSum;
		if(sum<rightSum) sum=rightSum;   //取三者的较大者		 
	}
	
	return sum;
	
	
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值