• 最大子段和之分治法


    最大子段和之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
    ",Sum);
        printf("构成最大和的数值:");
        for(i=sitel; i<=siter; i++)
            printf("%4d",a[i]);
    }
    

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

  • 相关阅读:
    EF中,保存实体报错:Validation failed for one or more entities. 如何知道具体错误在哪?
    个人常用的移动端浅灰底index.html
    分享一段js,判断是否在iPhone中的Safari浏览器打开的页面
    2017年3月25日,祝自己23岁生日快乐!
    JavaScript监控页面input输入整数且只能输入2位小数
    iPhone IOS10安装APP没提示连接网络(无法联网)的解决办法
    iPhone屏蔽IOS更新、iPhone系统更新的提示(免越狱,有效期更新至2021年)
    iPhone Anywehre虚拟定位提示“后台服务未启动,请重新安装应用后使用”的解决方法
    C# OracleDBhelper
    戴尔灵越15-5000/3558等系列修改BIOS设置U盘启动
  • 原文地址:https://www.cnblogs.com/Indomite/p/14195236.html
Copyright © 2020-2023  润新知