最大子段和问题

一.分治算法思路

给定n个整数组成的序列 a1,a2,…,an,求该序列的字段和的最大值。当所有整数均为负数的时定义其最大子段和为0。

如序列: 2,4,-3,5,-1,-7,6
该序列的最大子段为:2,4,-3,5
最大字段和为:8

对于这个问题,我们可以使用分治思想,将a[1:n]分成对等的两段a[1:n/2]和a[n/2+1:n],这样这个问题便被分解了三部分:

1. a[1:n]最大子段和与a[1:n/2]最大子段和相同
2. a[1:n]最大子段和与a[n/2+1:n]最大子段和相同
3. a[1:n]最大子段和位于数组的中间部分
前两种情况可以使用递归调用获得,而第三种情况则需要以分界点为界,分别向两边计算,最后将两边计算的结果进行相加。最后将三种结果进行比较,最终输出最大子段和。

例如:-2,11,-4,13,-5,-2
以n/2为分界点:
-2,11,-4
13,-5,-2
左边最大子段和为:11
右边最大子段和为:13
中间段最大子段和为:20
最后输出 20

二.代码实现

#include<iostream>
using namespace std;
int MaxSum(int a[],int left,int right,int *left_Partion,int *right_Partion)
{
    int center,sum_left,sum_right,sum;
    if (left==right)
    {
        if (a[left]<0)
        {
           return 0;
           *left_Partion = left;  
           *right_Partion = left;  
        }
        else
        return a[left];
               
    }
    else 
    {
        center=(left+right)/2;
        sum_left=MaxSum(a,left,center,left_Partion,right_Partion);
        sum_right=MaxSum(a,center+1,right,left_Partion,right_Partion);
        //求中间子段
        int s1=0,lefts=0;         
        for (int i = center; i >= left; i--)
        {
            lefts=lefts+a[i];
            if (s1<lefts)
            {
                s1=lefts;
                *left_Partion=i;
            }   
        }
        int s2=0,rights=0;
        for (int i = center+1; i < right; i++)
        {
            rights=rights+a[i];
            if (s2 < rights)
            {
                s2=rights;
                *right_Partion=i;
            }
            
        }
        sum=s1+s2;
        //比较大小
        if ((sum_left>sum_right)&&(sum_left>sum))
        {
            return sum_left;
        }
        else if (sum_left>sum)
        {
            return sum_right;
        }
        return sum;
    }
}
int main(int argc, char const *argv[])
{
    cout <<"请输入数组个数:";
    int n;
    cin >> n;
    int a[n];
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    int sum=0;
    int x,y;
    sum=MaxSum(a,0,n,&x,&y);
    cout << sum <<endl;
    for (int i = x; i <= y; i++)
    {
        cout << a[i] <<"\t";
    }
    cout << endl;
    system("pause");
    return 0;
}

三.动态规划算法思路及代码实现

重新定义一个数组b[n],令b[i]=b[i-1]+a[1],若b[i]<0则b[i]=0。最大子段和的值即为b[n]数组的最大值。

将这串数字的和存入数组b
-2 11 -4 13 -5 -2
b[1]=a[1]=-2<0
b[2]=b[1]+a[2]=11
b[3]=b[2]+a[3]=7
b[4]=b[3]+a[4]=20
b[5]=b[4]+a[5]=15
b[6]=b[5]+a[6]=13

#include<iostream>
using namespace std;
int MaxSum(int a[],int c[],int n,int *p)
{
    c[0]=0;
    for (int i = 1; i <= n; i++)
    {
        c[i]=c[i-1]+a[i];
        if (c[i]<0)
        {
            c[i]=0;
        }
        cout<<"i="<<i<<" "<<c[i]<<endl;//输出每个c[i]的值
    }
    int max=c[1];
    for (int i = 2; i <= n; i++)
    {
        if (max<c[i])
        {
            max=c[i];
            *p=i;
        }
    }
    return max;
}
int main(int argc, char const *argv[])
{
    cout <<"请输入数组个数:";
    int n,p;
    cin >> n;
    int *a=new int [n];
    int *c=new int [n];
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    int result=MaxSum(a,c,n,&p);
    cout << result <<endl;

    int s=0,i;
	for(i=p;i>=0;i--)
    {
    	s+=a[i];
		if(s==result && i>=0)
		   break;
	}//从c[i]最大的位置向前寻找,找到子段和的第一个位置
	if(i<0) i=0;
    cout<<i<<"-"<<p<<endl;
    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值