zoukankan      html  css  js  c++  java
  • 最大子段和之分治法

    最大子段和之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]);
    }
    

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

  • 相关阅读:
    运行带参数的python脚本
    调用其他python脚本文件里面的类和方法
    快速学会在JSP中使用EL表达式
    理解maven项目的pom.xml文件中,<scope>标签的作用——作用域以及依赖传递
    理解JavaWeb项目中的路径问题——相对路径与绝对路径
    hdu4417(Super Mario)—— 二分+划分树
    hdu4325-Flowers-树状数组+离散化
    poj3321-Apple Tree(DFS序+树状数组)
    数据的离散化
    POJ2676 – Sudoku(数独)—DFS
  • 原文地址:https://www.cnblogs.com/Indomite/p/14195236.html
Copyright © 2011-2022 走看看