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

来源:互联网 发布:中国gdp增速放缓知乎 编辑:程序博客网 时间:2024/06/16 06:06

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])

0 0
原创粉丝点击