关于最大子段和的几种解法

1、枚举法 n的三次方

#include<iostream>
using namespace std;
const int MAXN=100001;
int main()
{
    int a[MAXN];
    int n,i,j,k;
    int maxsum,temp;
    cin>>n;
    for(i=1;i<=n;i++) cin>>a[i];
    maxsum=a[1];
    for(i=1;i<=n;i++)    //枚举起点
      for(j=i;j<=n;j++)    //枚举终点
      {
          temp=0;
          for(k=i;k<=j;k++)    //枚举每种可能的值
             temp=temp+a[k];
          if(maxsum<temp) maxsum=temp;
      }
     cout<<maxsum;
    return 0;
}

2、预处理法  n的二次方

#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=100001;
int main()
{
    int s[MAXN],a[MAXN];
    memset(s,0,sizeof(s));
    int n,i,j;
    int maxsum,temp;
    cin>>n;
    for(i=1;i<=n;i++) 
    {
      cin>>a[i];s[i]=s[i-1]+a[i];   //预处理每项的前缀和
    }
    maxsum=a[1];
    for(i=1;i<=n;i++)      //枚举起点
      for(j=i;j<=n;j++)    //枚举终点
      {
          temp=s[j]-s[i-1];
          if(maxsum<temp) maxsum=temp;
      }
     cout<<maxsum;
    return 0;
}

3、分治法

算法描述如下

针对最大子段和这个具体问题本身的结构,我们还可以从算法设计的策略上对上述O(n^2)计算时间算法进行更进一步的改进。从问题的解结构也可以看出,它适合于用分治法求解。

如果将所给的序列a[1:n]分为长度相等的两段a[1:n/2]和a[n/2+1:n],分别求出这两段的最大子段和,则a[1:n]的最大子段和有三种情况:

(1) a[1:n]的最大子段和与a[1:n/2]的最大子段和相同

(2) a[1:n]的最大子段和与a[n/2+1:n]的最大子段和相同

(3) a[1:n]的最大子段和为a[i]+…+a[j],并且1<=i<=n/2,n/2+1<=j<=n。

对于(1)和(2)两种情况可递归求得,但是对于情况(3),容易看出a[n/2],a[n/2+1]在最大子段中。因此,我们可以在a[1:n/2]中计算出s1=max(a[n/2]+a[n/2-1]+…+a[i]),0<=i<=n/2,并在a[n/2+1:n]中计算出s2= max(a[n/2+1]+a[n/2+2]+…+a[i]),n/2+1<=i<=n。则s1+s2为出现情况(3)的最大子段和。据此可以设计出最大子段和问题的分治算法如下:

#include<stdio.h>
#define MAX 100
int maxsub(int left,int right);
int a[MAX];
int main()
{
    int i;
    int count;
    scanf("%d",&count);
    for(i=0;i<count;i++)
    {
        scanf("%d",&a[i]);
    }
    printf("%d\n",maxsub(0,count-1));
    return 0;
}
int maxsub(int left,int right)
{
    int center,i;
    int sum,left_sum,right_sum;
    int left_max,right_max;
    center=(left+right)/2;
    if(left==right)
        return a[left]>0?a[left]:0;
    else
    {
        left_sum=maxsub(left,center);
        right_sum=maxsub(center+1,right);
        sum=0;
        left_max=0;
        for(i=center;i>=left;i--)
        {
            sum+=a[i];
            if(sum>left_max)
                left_max=sum;
        }
        sum=0;
        right_max=0;
        for(i=center+1;i<=right;i++)
        {
            sum+=a[i];
            if(sum>right_max)
                right_max=sum;
        }
        sum=right_max+left_max;
        if(sum<left_sum)
            sum=left_sum;
        if(sum<right_sum)
            sum=right_sum;
    }
    return sum;
}

4、动态规划

设连续子段和为t, 则 当前t={  t+a[i];(t>=0)  a[i](t<0)

#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=100001;
int main()
{
    int s[MAXN],a[MAXN];
    int n,i;
    int ans,t;
    cin>>n;
    for(i=1;i<=n;i++)       cin>>a[i];
    ans=a[1];
    t=ans;
    for(i=2;i<=n;i++)
    {
        if(t>=0) t=t+a[i];
        else
           t=a[i];
        if(t>ans)ans=t;
    }
    cout<<ans;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_23215733/article/details/81566413
今日推荐