最大子段和之分治法

最大子段和之C语言

问题描述:
给定一个数组,找出其中可以构成最大数的子段,需要注意的是,这个不同于最大子序列求和
—— 最大字段求和:字段必须是连续的
—— 最大子序列求和:子序列只要是包含在原来的序列中即可
举个例子:

-1 4 -3 1 5 -1 4 -5 2

求上述的数组中的最大字段和,不难得知,最大子段和就是 10 ,也就是子段 4 -3 1 5 -1 4

思路:
首先,枚举?没有枚举解决不了的问题好吧。但是真的枚举么?在时间和空间复杂度上都会有很大的消耗;
分治?该怎么分治呢?由于在一个子段中,分治的话必须将原来的数组划分成几个部分,在本题中,大致有三种情况:

  1. 当最大子段和在所选的界定值左边的时候
  2. 当最大字段和在所选的界定值右边的时候
  3. 当最大子段和包含所选的界定值,也就是在界定值的两侧的时候

在第一种情况下,当最大子段和位于数组最左边的时候,通过不断地递归,保留最大的子段和,最后相加便可得到
同样,在第二种情况下,也就是说最大子段和在右边的时候,类似与上面的,通过不断地递归,相加可以求得
第三种情况相对来说比较复杂,当界定值包含在最大子段和中的时候,看上去就是类似于与问题了,但是,根据上述的想法,取得的界定值往左右两边分别寻找,相加便可以求得

在每一个划分的区间,都是采用上述三个步骤,可以得到 MAX Sum

代码演示:

#include<stdio.h>
#include<stdlib.h>
#define n 5
int a[n];
int MaxSum(int a[],int l,int r, int *sitel, int *siter)
{
    int c;  //中间位置
    int lsum,rsum,csum; //左边、右边、中间最大和
    if(l==r)
    {
        *sitel=l;
        *siter=l;
        return a[l];
    }
    else
    {
        c=(l+r)/2;
        //递归求左右两边的最大字段和
        lsum=MaxSum(a,l,c, sitel, siter);    //左边最大字段和
        //左边大时的位置临时保存
        int ltemp_sitel, ltemp_siter;
        ltemp_sitel=*sitel;
        ltemp_siter=*siter;
        rsum=MaxSum(a,c+1,r, sitel, siter);  //右边最大字段和
        //右边大时的位置临时保存
        int rtemp_sitel, rtemp_siter;
        rtemp_sitel=*sitel;
        rtemp_siter=*siter;
        //计算中间最大字段和
        //求左半部份
        int i;
        int csuml=0,cleft=0;
        int templ=c;
        for(i=c;i>=l;i--)
        {
            cleft=cleft+a[i];
            if(cleft>csuml){
                csuml=cleft;
                templ=i;
            }
        }
        //求右半部份
        int j;
        int csumr=0,cright=0;
        int tempr=c;
        for(j=c+1;j<=r;j++)
        {
            cright=cright+a[j];
            if(cright>csumr){
                csumr=cright;
                tempr=j;
            }
        }
        //中间最大和
        csum=csuml+csumr;

        //位置确定
        if(csum>lsum)
            if(csum>rsum){  //中间最大
                *sitel=templ;
                *siter=tempr;
                return csum;
            }else{  //右边最大
                *sitel=rtemp_sitel;
                *siter=rtemp_siter;
                return rsum;
            }else if(lsum>rsum){ //左边最大
            	*sitel=ltemp_sitel;
            	*siter=ltemp_siter;
            	return lsum;
        	}
    		}
}

void input()
{
    int i;
    printf("请输入一组数字:");
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
}
void main()
{
    int Sum;
    int sitel=0;
    int siter=n-1;
    int i;
    input();
    Sum=MaxSum(a,0,n-1,&sitel, &siter);
    printf("最大和为:%4d\n",Sum);
    printf("构成最大和的数值:");
    for(i=sitel; i<=siter; i++)
        printf("%4d",a[i]);
}

结果如下所示:
在这里插入图片描述
小结:
在分治法解决问题的时候,不断地拆分问题,将问题拆分成我们可以解决的问题即可,类似于上题,将问题最后拆分成最小的子集,可以直接判断最大子段和
递归的思想还是很重要!!!

  • 13
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值