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 的最优值
然后判断 左面,右面,中间哪个大就是该串的最大连续子串和
如此分治下去
C++是参考别人的代码:

#include<stdio.h>
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;
}

下面是自己的C代码,动态规划:
用数组a[]记录序列中的数,对于a[i]只有两种可能
1.为一个序列的首  
2.为一个序列的尾  
用数组d[i]记录以第i个数结尾的序列的最大和,
则d[i]=max(d[i-1]+a[i],a[i]),d[i-1]+a[i]和a[i]分别对应a[i]的两种情况。

#include<stdio.h>
 #include<string.h>
 int a[100000+11];
 int d[100000+11];
int max(int a,int b)
 {
  return a>b?a:b;
 }
 int main()
 {
     int t,i;
     scanf("%d",&t);
     int k=0;
     while(t--)
     {
         int sum=0;
         int n,begin,end;    
         int max0=-1001;                            //max0必须小于所有可能的整数 
         scanf("%d",&n);
         memset(a,0,sizeof(a));
        memset(d,0,sizeof(d));
         for(i = 1 ; i <= n ; i++)
         {
             scanf("%d",&a[i]);
             d[i]=max(d[i-1]+a[i],a[i]);
             if(max0 < d[i])
             {
                 max0=d[i];                        //记录序列和的最大值 
                 end=i;                            //记录和最大的序列的尾 
             }
        }
         for(i = end ; i >= 1 ; i--)
         {
             sum+=a[i];
             if(sum == max0)
             {
                 begin=i;
             }
                 
         }
        printf("Case %d:\n",++k);
        printf("%d %d %d\n",max0,begin,end);
         if(t)
         {
             printf("\n");                //注意输出格式 
         }
         
     }
}

猜你喜欢

转载自blog.csdn.net/qq_44009311/article/details/86761188