这道题是学校寒假div1上的题目。这道题当时并没有做出来,并且之后卡了很久,因此特意在这里总结一下问题。
算法一:
这个算法是我参考书上的归并算法写的。
首先我想着重说的是第十二行程序执行顺序的问题。
我原先的理解是先运行逗号左边的内容,再运行逗号右边的内容,后来发现这是不正确的。
经过测试程序先递归执行了程序右边的函数,因此得到的序号数比较大;正是由于这个原因,当遇到相同的最大值时,得到的下标总是最小的(确保29,36行运算符为>=)。
除此之外,我这道题存在的问题就是对递归归并算法不熟悉,搞不清执行的情况,因而导致逻辑出了问题,后来通过加maxsum 变量暂存目前最大值来解决左右区间得到最大值但互不知情的情况,这样就实现了正确更新左右序号。
1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 int lefts,rights; 5 long long A[10000 + 100]; 6 int maxsum = 0; 7 long long max_sum( long long *A,int x,int y){ 8 long long v,L,R,maxs; 9 if(y-x==1) 10 return A[x]; 11 int m=x+ (y-x)/2; 12 maxs = max(max_sum(A,x,m),max_sum(A,m,y)); 13 v=0; 14 int l=m-1,r=m; 15 L=A[m-1]; 16 for(int i=m-1;i>=x;i--){ 17 long long t= (v+=A[i]); 18 if(t>=L)l=i; 19 L=max(L,t); 20 } 21 v =0; 22 R=A[m]; 23 for(int i=m;i<y;i++){ 24 long long t= (v+=A[i]); 25 if(t>R)r=i; 26 R=max(R,t); 27 } 28 if(L+R>maxs){ 29 if(L+R>=maxsum){ 30 maxsum=L+R; 31 lefts=l;rights=r; 32 } 33 return L+R; 34 } 35 else{ 36 if(maxs>=maxsum){ 37 maxsum=maxs; 38 if(y-m==1&& maxs==A[m]) 39 rights=lefts=m; 40 else if(m-x==1&& maxs==A[x]) 41 lefts=rights=x; 42 } 43 return maxs; 44 } 45 } 46 int main(){ 47 int n; 48 while(scanf("%d",&n)==1&&n){ 49 maxsum = -1; 50 lefts=0,rights=n-1; 51 int fu=0; 52 for(int i=0;i<n;i++){ 53 scanf("%lld",&A[i]); 54 if(A[i]<0)fu++; 55 } 56 if(fu!=n){ 57 long long m=max_sum(A,0,n); 58 printf("%lld %lld %lld ",m,A[lefts],A[rights]); 59 } 60 else 61 printf("0 %lld %lld ",A[0],A[n-1]); 62 } 63 return 0; 64 }
算法二:
这道题是从我女朋友那里搞来的算法,自己一开始理解起来比较困难。但经过了较长时间的思考推理,大概理解了这个算法。不得不说这个算法比我自己用的算法简洁多了。
但是我感觉不是思路很好想,并运行耗时更多(不知道为什么这个在OJ上 提交运行时间会不稳定,一次超时,再提交就AC了)。
对于这道题为什么可以这样写,我想根据sum值讨论一下。
1.假设任意处sum>=0.
假设以上图形代表最优解范围起点的两种可能性,一种是开始于a点,一种是开始于b点。假设从b点开始,因为sum>=0恒成立,所以A范围内数字大于等于0,所以A+B>=B,又因题目要求序号尽量小,所以我们的最优解范围是开始于a点。
2.假设存在一段sum<0
如图所示,假设B区域sum值都为负(即都为负数),假设最优解开始于a点,因为A+B<0,所以A+B+C区域的sum值一定小于C,所以最优解一定开始于c点。
综上,我们可以写出下面的代码
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 int main() 6 { 7 int t; 8 while(cin >> t &&t) 9 { 10 int a[100005]; 11 int ans = -100000, sum = 0, fir = 0, la = 0, q = 0; 12 for(int i = 0; i < t; i++) 13 cin >> a[i]; 14 for(int i = 0; i < t; i++) 15 { 16 if(sum < 0) {sum = a[i];q = i;} 17 else sum += a[i]; 18 if(sum > ans) 19 { 20 ans = sum; 21 la = i; 22 fir = q; 23 } 24 } 25 if(ans < 0) {ans = 0; fir = 0; la = t-1;} 26 printf("%d %d %d ", ans, a[fir], a[la]); 27 } 28 return 0; 29 }
目前只能理解到这个程度,如有不当之处还望指教。