7-1 最大子列和问题 (20分)【分治递归+联机算法】

7-1 最大子列和问题 (20分)【分治递归+联机算法】

7-1 最大子列和问题 (20分)
题目描述:
题目描述

输入格式:
输入第1行给出正整数K (≤100000);第2行给出K个整数,其间以空格分隔。

输出格式:
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0

输入样例:

6
-2 11 -4 13 -5 -2

输出样例:

20

《数据结构与算法分析——C语言描述》学习
首先,第一种方法:递归算法采用了“分治”策略。(O(N log N)分:把问题分成两个大致相等的子问题,然后递归对他们求解。治:将两个子问题的解合并在一起并可能再做些少量的附加工作,最后得到整个问题的解。

分治思路:
先列出分治基准,就是我们要知道它什么时候这个递归要停下来
再列出递归,不断的调用这个函数,求出左半部分的最大子序列和&右半部分的最大子序列和
再求左半部分包括最后一个元素的最大值和右半部分包括第一个元素的最大值,比较左半部分的最大子序列和,右半部分的最大子序列和 和 左半部分包括最后一个元素的最大值+右半部分包括第一个元素的最大值(横跨两部分且通过中间的和)
如果还是不太理解,可以跟着程序走一遍,就会清楚很多了。

#include<bits/stdc++.h>
using namespace std;
int Max3(int a, int  b, int  c)//求三个数中的最大值
{
    if ( a >= b && a >= c ) return a;
    else if ( b > a && b > c ) return b;
    else  return c;
}


int MaxSubSum(int a[] , int left , int right)  //分治算法
{
    int MaxLeftSum, MaxRightSum;//左半部分的最大子序列和/右半部分最大子序列和
    int MaxLeftBorderSum, MaxRightBorderSum;//左半部分包括最后一个元素的最大和/右半部分包括第一个一个元素的最大和
    int LeftBorderSum, RightBorderSum;
    int Center ,i;

    if(left==right)     //基准,当left=right的时候代表递归结束
        if(a[left]>0)   //题目要求大于0
        return a[left]; //这时候a[left]=a[right],所以随便返回哪个值都可以
    else
        return 0;     //答案若为负数,则输出0

    Center = (left + right) / 2;
    MaxLeftSum = MaxSubSum(a , left , Center );
    MaxRightSum = MaxSubSum(a , Center + 1 ,right ); //递归

    MaxLeftBorderSum = 0;LeftBorderSum = 0;
    for(i=Center; i>=left; i--)       //左半部分包括最后一个元素的最大值
    {
        LeftBorderSum += a[i];
    if(LeftBorderSum > MaxLeftBorderSum)
        MaxLeftBorderSum = LeftBorderSum;

    }

     MaxRightBorderSum = 0;RightBorderSum = 0 ;
     for(i=Center+1; i<=right; i++)      //右半部分包括第一个元素的最大值
    {
        RightBorderSum+=a[i];
    if(RightBorderSum > MaxRightBorderSum)
        MaxRightBorderSum = RightBorderSum;
    }
    return Max3(MaxLeftSum, MaxRightSum , MaxLeftBorderSum + MaxRightBorderSum );  //最大子序列和
}


int main()
{
    int k,ans;
    scanf("%d",&k);
    int a[k];
    for(int i=0;i<k;i++)
    {
        scanf("%d",&a[i]);
    }
    ans=MaxSubSum(a,0,k-1);
    printf("%d\n",ans);
    return 0;
}

第二种方法:联机算法(更好)
这种算法实现起来比递归算法简单而且更为多效
书上说该算法的附带优点是:它只对数据进行一次扫描,一旦完成对A[i]的读入和处理,就不需要再记忆它了。
仅需要常量空间和线性时间运行的联机算法几乎是完美的算法。

解题思路:
先进行初始化,赋0.再从头开始累加,加到大于最大子序列和的值,把这个值给MaxSum,如果累加为负数,这一小段的最大子序列就已经出来了,可以重新进行累加比较了,最后MaxSum一定为最大子序列的和。
*
*

#include<bits/stdc++.h>
using namespace std;


int MaxSuseuenceSum(int a[] , int N)  //联机算法
{
      int ThisSum , MaxSum , j;

      ThisSum = MaxSum = 0;
      for (j = 0;j < N; j ++) //从头开始累加
      {
          ThisSum +=a[j];

          if(ThisSum > MaxSum) //加到大于max的值,给max赋值
            MaxSum = ThisSum;
          else if(ThisSum < 0 ) //当thissum<0时,它就没有机会再大于max了,直接初始化,从0开始重新累加
            ThisSum = 0;
      }
      return MaxSum;  //最后max的值就是最大子序列
}


int main()
{
    int  k , ans;
    scanf("%d" , &k);  //要输入k
    int a[k];         //才能定义a[k]
    for(int i = 0;i < k; i ++)
    {
        scanf("%d", &a[i]);
    }
    ans = MaxSuseuenceSum(a , k);
    printf("%d\n" , ans);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45633496/article/details/107577472