题意
需要在o(n)时间内,求最大连续的子序列的和,及其起点和终点。
分析
一种方法是一边读,一边维护最小的前缀和 s[i] ,然后不断更新 ans = max(ans,s[j] - s[i]),以及起始位置。
另一种方法是尺取(算是吧),l 和 r 代表起点和终点,一开始l=0,r=1,如果s[r]-s[l]>=0那就往右扫 r++,不断更新 ans = max(ans,s[r] - s[l]),以及起始位置,如果小于0了,那就舍弃前面这段了,也就是后面必然不用考虑它更划算,l=r,r++。还要注意全是负数的情况,那就只要输出最大的那个负数。
代码
维护min(s[i])
#include <cstdio> #include <algorithm> using namespace std; int test,n; int a,k,ans,s[100005],st,en; int main() { scanf("%d",&test); for(int t=1; t<=test; t++) { s[0]=0; k=0; ans=-1001; scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d",&a); s[i]=s[i-1]+a; if(s[i]-s[k]>ans){ ans=s[i]-s[k]; en=i; st=k+1; } if(s[i]<s[k]){ k=i; } } if(t<test)printf("Case %d: %d %d %d ",t,ans,st,en); else printf("Case %d: %d %d %d ",t,ans,st,en); } return 0; }
尺扫
#include <cstdio> #include <algorithm> using namespace std; int test,n,a,ans,s[100005],st,en,maxa,maxp; int main() { scanf("%d",&test); for(int t=1; t<=test; t++) { maxa=-1001; ans=0; scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d",&a); s[i]=s[i-1]+a; if(a>maxa) { maxa=a; maxp=i; } } if(maxa<0) { ans=maxa; st=en=maxp; } else { int l=0; int r=1; while(r <= n) { while(s[r] >= s[l] && r<n) { r++; if(s[r]-s[l]>ans) { ans=s[r]-s[l]; st=l+1; en=r; } } l=r; r++; } } if(t<test)printf("Case %d: %d %d %d ",t,ans,st,en); else printf("Case %d: %d %d %d ",t,ans,st,en); } return 0; }