算法之最大字段和问题

236 阅读2分钟

这是我参与 8 月更文挑战的第 4 天,活动详情查看: 8月更文挑战

要求:

1.随机给出一个整数序列(可能有负整数),选出其中连续且 非空的一段使得这段和最大。

2.使用分治法解决。

实验过程:

一、穷举法

#include<iostream>
using namespace std;

const int n=10;

int MUL(int p[]){
    int sum=0,max=0;

    for(int i=0;i<n;i++){
        if(p[i]>0){//定位到序列第一个正数
        for(;i<n;i++){
            sum+=p[i];
            if(sum>max)
            max=sum;
        }
        }
    }
    return max;

}
int main(){
    int s[n];
    cout<<"请输入长度为"<<n<<"的序列"<<endl;
    for(int i=0;i<n;i++){
        cin>>s[i];
    }
    cout<<"最大字段和为:"<<MUL(s)<<endl;
    system("PAUSE");
    return 0;
}

时间复杂度为o(n2)

截屏2021-08-05 下午7.11.54

二、分治法

基本思想:

将数组从中间一分为二

(1)递归求得左子段的最大序列和leftsum

(2)递归求得右子段的最大序列和rightsum

(3)考虑最大序列和可能横跨mid,从mid开始,向左相加求得最大总和sl,再从mid向右求得最大总和sr,两者相加所得再与leftsum和rightsum相比较,所得便是最大字段和。

具体代码实现如下:

#include<iostream>
using namespace std;

const int n=10;

int MUL2(int s[],int left,int right){
    int sum;
    if(left==right)
     return s[left];
        int mid = (left+right)/2;
        int leftsum = MUL2(s,left,mid);//求左边最大值
        int rightsum = MUL2(s,mid+1,right);//求右边最大值
    //求横跨mid的最大区间和
    //求从mid向左相加的区间和
        int sl=s[mid];
        int lefts = 0;
        for(int i=mid;i>=left;i--)
        {
            lefts += s[i];
            if(lefts > sl)
            sl=lefts;
        }

        int sr=s[mid+1];
        int rights=0;
        for(int i=mid+1;i<=right;i++)
        {
            rights += s[i];
            if (rights > sr)
              sr=rights;
        }
        //分治法三种情况比较,确定整个序列的最大字段和
        sum = sl+sr;
        if(sum<leftsum)
           sum=leftsum;
        if(sum<rightsum)
           sum=rightsum;
    return sum;
} 

int main(){
    int s[n];
    cout<<"请输入长度为"<<n<<"的序列"<<endl;
    for(int i=0;i<n;i++){
        cin>>s[i];
    }
    cout<<"最大字段和为:"<<MUL2(s,0,n-1)<<endl;
    system("PAUSE");
    return 0;
}

时间复杂度为o(nlogn)

三、动态规划

基本思想:

截屏2021-08-05 下午7.12.47

#include<cstdio> //比起cin cout更快一些
#include<algorithm>
using namespace std;

const int maxn=2e5+20;//给数组设计一个很大的值200020
int a[maxn],f[maxn];
int m=-0x3f3f3f3f; //初始化最大值,因为数组中数可能都为负值,所以必须选一个无穷小的数
int main(){
    int n;
    printf("请输入数组长度:");
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
        if(f[i-1]>=0){
        f[i]=f[i-1]+a[i];
        }
        else 
        f[i]=a[i];
        m=max(m,f[i]);
    }
    printf("%d",m);
    system("pause");
    return 0;
}