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]);
    }
    

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

  • 相关阅读:
    golang-switch结构辨析有话
    不用中间变量交换变量值-golang版
    vue element ui表单验证不通过,滚动到页面上第一个验证失败的输入框位置
    表单校验中使用v-if和v-else来判断是福哦要校验时的注意项
    如何修改本地项目关联的远程仓库地址
    vue-cli3如何访问public文件夹下的静态资源
    Git 命令行的各种退出方式
    elementui表格如何在表头每个列标题后面插入图片用于插入tooltip
    js 把一个二叉树类型的对象转化为普通对象
    element-ui树结构设置默认选中节点时改变传入的数组树结构没有变化
  • 原文地址:https://www.cnblogs.com/Indomite/p/14195236.html
Copyright © 2011-2022 走看看