Max Sum(hdu1003最大连续子串和+分治法)

题意:最大连续子串和,并保存下标

思路:分治法

a1,a2,a3.................an的最大连续子串和用分治

先将序列分成a1,a2,a3.....an/2, 和 an/2+1,an//2+2...........an

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[1:n/2],a[n/2+1:n]组成

1和2这两种情况可递归求得,对于情形3,容易看出a[n/2]与a[n/2+1]在最优子序列中(因为最大子段和不在左面,也不在右面,只能在中间段,在中间一定包含a[n/2]与a[n/2+1])

所以情况3就可以 算出 s1 = a[n/2,1]的最大连续子串和,s2 = a[n/2+1,n],s1+s2就是情况3 的最优值

然后判断 左面,右面,中间哪个大就是该串的最大连续子串和

如此分治下去

#include
#include
#include
using namespace std;
int a[100005] = {0};
struct Max
{
    int sst;
    int eend;
}M[400010];
int iMax_a,iMax_b;

int MaxSubSum(int left,int right,int flag)
{
    int sum = 0;
    if(left == right)
    {
        sum = a[left] ;
        M[flag].sst = left;
        M[flag].eend  = left;
       //printf("%d %d %d\n",M[flag].sst,M[flag].eend,flag);
    }
    else
    {
        int mid = (left + right) / 2;
        int leftsum = MaxSubSum(left,mid,2*flag);
        int rightsum = MaxSubSum(mid+1,right,2*flag+1);
        int s1 = -9999999;
        int Lsum = 0;
        int i;
        for(i = mid; i >= left; i--)
        {
            Lsum += a[i];
            if(Lsum > s1)
            {
                s1 = Lsum;
                M[flag].sst = i;//printf("1%d %d\n",M[flag].sst,flag);
            }
        }
        int s2 = -9999999;
        int Rsum = 0;
        for(i = mid+1;i <= right; i++)
        {
            Rsum += a[i];
            if(Rsum > s2)
            {
               s2 = Rsum;
               M[flag].eend  = i;
            }//printf("%d %d\n",M[flag].eend,flag);
        }
        sum = s1+s2;
        if(sum < leftsum)
        {
            sum = leftsum;
            M[flag].sst = M[2*flag].sst;
            M[flag].eend  = M[2*flag].eend;
           // printf("%d %d\n",M[flag].sst,M[flag].eend);
        }
        if(sum < rightsum)
        {
            sum = rightsum;
            M[flag].sst = M[2*flag+1].sst;
            M[flag].eend  = M[2*flag+1].eend;
        }
    }
    return sum;
}
int main()
{
    int t,k = 0;
    scanf("%d",&t);
    while(t--)
    {
        int i,n;
        scanf("%d",&n);
        for(i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
        }
        int len = MaxSubSum(1,n,1);
        printf("Case %d:\n",++k);
        printf("%d %d %d\n",len,M[1].sst,M[1].eend);
        if(t != 0)
            printf("\n");
    }
    return 0;
}

讲一下怎么保存下标:

因为是递归所以同一个变量的值会不断被覆盖,所以不能用全局变量保存

然后根据分治法和二叉树的特点,可以根据分治法建立一个结构体组成节点的二叉树

结点信息保存分治的某一段字符串最大连续子串和的开始和结束下标

struct Max
{
    int sst;
    int eend;
}M[400010];

结点1

    a[1---n] 最大连续子段和的下标

结点2 结点3

a[1----n/2]最大连续子段和的下标 a[n/2+1----n]最大连续子段和的下标

重点讲怎么更新

因为结点1的下标 由结点2,结点3和结点2和结点3结合组成,

所以在比较中间值和左右值的时候更新下标

如果  中间值小于左面的值

说明最大连续子段和出现在左面,将结点1的下标更新成结点2的下标

同理,

如果  中间值小于右面的值

说明最大连续子段和出现在右面,将结点1的下标更新成结点3的下标

那中间的呢?

我们可以根据二叉树递归建树的特点让结点1保存中间最大连续子段和的下标

这样如果中间值大于左面和右面的值,那结点1保存的就是中间的最大连续子段和的下标

不停的分治

最后只要输出结点1的开始结束下标就ok了

累死了~~~写的不好,大家凑合看


你可能感兴趣的:(分治算法)