基础算法--B最大字段和

Description

Given a sequencea[1],a[2],a[3]......a[n], your job is to calculate the max sum of asub-sequence. For example, given (6,-1,5,4,-7), the max sum in this sequence is6 + (-1) + 5 + 4 = 14.

Input

The first lineof the input contains an integer T(1<=T<=20) which means the number oftest cases. Then T lines follow, each line starts with a numberN(1<=N<=100000), then N integers followed(all the integers are between-1000 and 1000).

Output

For each testcase, you should output two lines. The first line is "Case #:", #means the number of the test case. The second line contains three integers, theMax Sum in the sequence, the start position of the sub-sequence, the endposition of the sub-sequence. If there are more than one result, output thefirst one. Output a blank line between two cases.

Sample Input

2

5 6 -1 5 4 -7

7 0 6 -1 1 -6 7-5

Sample Output

Case 1:

14 1 4


Case 2:

7 1 6

最大字段和一直都有很多做法:

1.      枚举算法

简单的枚举算法当然可以解决这个问题,枚举所有可能的起始下标(i=1,2,3……,n)和终止下标(j=1,2,3……,n),累加各种情况的元素和,并从中选出最大的字段和。

2.      二分策略算法

如果将所给的序列a[1:n]分为长度相等的两段a[1: (n/2)]]和b[(n/2)+1:n],分别求出这两段的最大字段和,则a[1:n]的最大字段和有3种情形。

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

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

(3),a[1:n] 的最大子段和为a[i:j],()

情形(1)和(2)可递归求得。

对于情形(3),序列中的元素a[(n/2)]与a[(n/2)+1]一定在最大子段中。因此可以算出a[i: (n/2)]的最大值S1;并计算出a[(n/2)+1:j]中的最大值S2。则S1+S2即为出现情况(3)时的最优值。

据此可设计最大子段和的分治算法求出最大子段。由于子问题不独立,不同于一般的二分治算法,这里算法的实质是“三”分治。

intmax_sum3(int a[],int n)

{

return max_sub_sum(a,1,n);

}

intmax_sub_sum(int a[],int left,int right)

{

intcenter,i,j,sum,left_sum,right_sum,s1,s2,lefts,rights;

if(left==right)

           if(a[left]>0)

                    return a[left];

           else

                    return 0;

else

{

           center=(left+right)/2;//二分中心位置

           left_sum=max_sub_sum(a,left,center);//求左部最大子段和

           right_sum=max_sub_sum(a,center+1,right);//求右部最大子段和

           s1=0;//处理情形3

           lefts=0;

           for(i=center;i>=left;i--)

           {

                    lefts=lefts+a[i];

                    if(lefts>s1)

                             s1=lefts;

           }

           s2=0;

           rights=0;

           for(i=center+1;i<=right;i++)

           {

                    rights=rights+a[i];

                    if(rights>s2)

                             s2=rights;

           }

           if(s1+s2<left_sum&&right_sum<left_sum)//情形1

                    return left_sum;

           if(s1+s2<right_sum)//情形2

                    return right_sum;

           return s1+s2;//情形3

}

}

3.      动态规划算法

方法2中由于分解后子问题不独立,情形3中重复计算较多,没有充分利用前期的计算结果。而动态规划的特长就是解决分解的子问题不独立的情况。用动态规划就是通过开辟存储空间,存储各个子问题的计算结果,从而避免重复计算。并且动态规划有很强的阶段递推思想,用前一阶段存储的计算结果递推后一阶段的结果。

记sum[i]为a[1]~a[i]的最大子段和,记this_sum[i]为当前子段和。

this_sum[i]从i=1开始计算,当this_sum[i-1]>=0时,前面子段的和对总和有作用,所以要累加当前元素的值;当this_sum[i-1]<=0时,则开始重新累加。

初值:this_sum[0]=0;i=1,2……,n时

this_sum[i]=this_sum[i-1]+a[i]   当this_sum[i-1]>=0

this_sum[i]=a[i]       当this_sum[i-1]<0

相应的sum[i]的递推定义式即:a[0]=0,i=1,2……,n时

sum[i]=sum[i-1]      当this_sum[i]<=sum[i-1]

sum[i]=this_sum[i]    当this_sum[i]>sum[i-1]

sum[i]在记录a[1]~a[i]的最大子段和。

 由此思想得出的完全代码:

#include <stdio.h> 

#include <stdlib.h>

#include <string.h>

int a[100001];

int maxSub(int *a,int n)  //求最大子段和函数

int i=0, max=0,max_pos=0;

   int si_1=0,si=0;

   int *p = (int *)malloc(n*sizeof(int));//通过p来记录this_sum[i-1]的状态

   if (p==NULL) 

       return -1; 

   max =si_1 = a[0];  //初始化max,sum[i-1]

   p[0] = 0;  //p[i]=0表示this_sum[i-1]<0

   for (i=1; i<n; i++)

         { 

       if (si_1<0){    //sum[i-1]<0的情况

           p[i] = 0; 

           si = a[i];  //si即sum[i]

       } else{ 

           p[i] = 1; 

           si = si_1+a[i];  //累加

       } 

       si_1 = si; 

       if (si>max){    //记录最大子段和

           max = si; 

           max_pos = i;  //记录最大子段终点

       } 

   } 

   for (i=max_pos; i>=0; i--) 

       if (p[i]==0)  //寻找最大字段起点

           break; 

   printf("%d %d %d\n", max,i+1,max_pos+1); 

   free(p); 

    p= NULL; 

   return max; 

 

int main() 

   int n,i,m,j;

         scanf("%d",&m);

         getchar();

         for(i=0;i<m;i++)

         {

                   scanf("%d",&n);

                   for(j=0;j<n;j++)

                            scanf("%d",&a[j]);//输入数列

                   printf("Case%d:\n",i+1);

   maxSub(a, n);

         if((i!=m-1))

         printf("\n");

         memset(a,0,sizeof(a));

         }

   return 0; 

}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值