[拓展]杭电1003(最大子数组问题)

http://acm.hdu.edu.cn/showproblem.php?pid=1003
就题目本身,不考虑时间限制,解法是很多的
首先
可以暴力求解,用三重循环解决
优化一次
省略一次循环,这里给出一段代码(在杭电上超时,还是需要进一步优化)

#include <stdio.h>
 int MaxSum(int* A,int n,int* i,int* j) 
 { 
     int maximum=-100000,sum,t,p; 
     for (*i=0;*i<n;(*i)++) 
     {
          sum=0;  
          for (*j=*i;*j<n;(*j)++)
           {
               sum+=A[*j]; 
               if (sum>maximum) 
               {    maximum=sum; t=*i;p=*j;
                }
           }
      }
       *i=t+1; *j=p+1; 
       return maximum; 
   } 
int main() 
{
    int n,i,j,m,a[100001],x,y;
    scanf("%d",&n);
    for (i=0;i<n;i++) 
    { 
        scanf ("%d",&m); 
        for (j=0;j<m;j++) 
            scanf ("%d",&a[j]);
            printf ("Case %d:\n%d %d %d\n\n",i+1,MaxSum(a,m,&x,&y),x,y); 
            } 
return 0; 
}

第二次优化
可以继续省略一次循环,使用分治策略 分解整个数组为两个子数组,A[low,mid],A[mid+1,high]
分三种情况
1. maximum位于A[low,mid]中
2. maximum位于A[mid+1,high]中
3. 跨越中点 low<=i<=mid<=j<=high 在此即可省略一次for,记录各段中相加的最大值,并不断更新

int leftsum=ptrA[mid];
//左部最大值 
int maxleft=mid;
//左边界 
int sum=0; 
for (int i=mid;i>=low;i--) 
{ 
   sum+=ptrA[i]; 
   if (sum>=leftsum) 
   { 
      leftsum=sum; maxleft=i; 
   } 
} 
//这里只给出左部的代码,还需写出贯穿时和右部的代码,不一一列举 
//其中,贯穿时,其范围是由上述第三点确定的 

变换思路,动态规划
至于为什么有人把优化一当成动态规划,我就不得而知了。。。

int max(int x,int y) 
{   
   return (x>y?x:y);
} 
int maxsum(int *A,int n) 
{ 
   start[n-1]=A[n-1]; 
   ALL[n-1]=A[n-1]; 
   for (i=n-2;i>=0;i--) 
   { 
      start[i]=max(A[i],A[i]+start[i+1]);
      ALL[i]=max(start[i],ALL[i+1]);               
   } 
return ALL[0]; 
}
//这里给出完整的代码 
#include<iostream> 
#define inf 0x3f3f3f using namespace std;
int a[100001],s[100001],t[100001]; 
int main() 
{ 
   int m,n,i,j,p,q,max; 
   cin>>n; 
   for(i=1;i<=n;i++) 
   { 
      if(i!=1) 
      cout<<endl; 
      cin>>m; 
      for(j=1;j<=m;j++) 
      cin>>a[j];  
      t[0]=-1; 
      s[0]=0; 
      for(j=1;j<=m;j++) 
      { 
         if(t[j-1]>=0) 
         { 
            t[j]=t[j-1]+a[j];
            s[j]=s[j-1];
         } 
         else 
         { 
            t[j]=a[j]; 
            s[j]=j; 
         } 
      } 
      max=-inf;
      p=0;
      q=0; 
      for(j=1;j<=m;j++) 
      if(t[j]>max) 
      { 
         max=t[j]; 
         p=s[j]; 
         q=j; 
      } 
   cout<<"Case "<<i<<":"<<endl; cout<<max<<" "<<p<<" "<<q<<endl;        } 
   return 0; 
} 

由于是第一次接触动态规划,所以最开始也看不懂,后来看了别人的分析,就清楚很多了,这里给出引用的链接http://alorry.blog.163.com/blog/static/6472570820123801223397/

对于整个序列a[n]来说,它的所有子序列有很多很多,但是可以将它们归类。
注意,是以结尾的子序列,其中肯定是要包含的了

以a[0]结尾的子序列只有a[0]
以a[1]结尾的子序列有 a[0]a[1]和a[1]
以a[2]结尾的子序列有 a[0]a[1]a[2] / a[1]a[2] / a[2]
……
以a[i]结尾的子序列有a[0]a[1]……a[i-2]a[i-1]a[i] / a[1]a[2]……a[i-2]a[i-1]a[i] / a[2]a[3]……a[i-2]a[i-1]a[i] / …… / a[i-1]a[i] / a[i]

所有以a[0] ~a[n]结尾的子序列分组构成了整个序列的所有子序列。

这样,我们只需求以a[0]~a[n]结尾的这些分组的子序列中的每一分组的最大子序列和。然后从n个分组最大子序列和中选出整个序列的最大子序列和。

观察可以发现,0,1,2,……,n结尾的分组中,
maxsum a[0] = a[0]
maxsum a[1] = max( a[0] + a[1] ,a[1]) = max( maxsum a[0] + a[1] ,a[1])
maxsum a[2] = max( max ( a[0] + a[1] + a[2],a[1] + a[2] ),a[2])
= max( max( a[0] + a[1] ,a[1]) + a[2] , a[2])
= max( maxsum a[1] + a[2] , a[2])
……
依此类推,可以得出通用的式子。
maxsum a[i] = max( maxsum a[i-1] + a[i],a[i])

你可能感兴趣的:(优化,杭电)