题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1231
给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= K。最大连续子序列是所有连续子序列中元素和最大的一个, 例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和 为20。 在今年的数据结构考卷中,要求编写程序得到最大和,现在增加一个要求,即还需要输出该 子序列的第一个和最后一个元素。
首先我们会想到n^2的做法就是先求出前缀和,然后枚举最大子序列的起点和终点。但此题数据范围是n<=10000,并且多组数据,所以n^2做法是会超时的。那么应该怎么优化那?
这道题正解是dp,dp【i】代表以i为结尾的最大子序列和,这样终点是固定的,而起点是随意的,那么我肯定找最优的起点,以2为例,如果第一个元素是正的,那么我一定会加到dp[2]上,如果是负的,那dp[2]最大就是a[2]同样的依次类推,dp[i]就可以求出以i为结尾的最大子序列和,这样如果dp【i-1】是负的,那么dp【i】=a[i](a[i]即原始序列的第i个元素),否则dp[i]=dp[i-1]+a[i],这样最后只需要在扫一遍dp数组就可以知道最大子序列和是多少了。
本题还要求输出起止两个元素,所以要在处理一下,代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int n; 5 int a[10005]; 6 int dp[10005]; 7 int s[100005]; 8 int ans; 9 int t1,t2; 10 int main() 11 { 12 while(1) 13 { 14 scanf("%d",&n); 15 if(n==0)break; 16 ans=-1; 17 for(int i=1;i<=n;++i)scanf("%d",&a[i]),s[i]=s[i-1]+a[i]; 18 for(int i=1;i<=n;++i) 19 dp[i]=max(dp[i-1]+a[i],a[i]); 20 t1=1,t2=n; 21 for(int i=1;i<=n;++i) 22 if(dp[i]>ans)ans=dp[i],t2=i; 23 if(t2==n&&ans==-1) 24 { 25 printf("0 %d %d ",a[1],a[n]); 26 }else 27 { 28 for(int i=1;i<=t2;++i) 29 if(s[t2]-s[i-1]==ans) 30 { 31 t1=i;break; 32 } 33 printf("%d %d %d ",ans,a[t1],a[t2]); 34 } 35 } 36 return 0; 37 }