柱爷爱思考,凡事喜欢举一反三,常常能想到别人没想过的问题。
比如最大区间和这个问题:在一数列上选出一段区间,使得这段区间和最大。
柱爷想:如果选出两段区间(不相邻)会怎样呢?
柱爷很快想到了答案,你呢?
第一行输入一个数N,表示数组的长度。
第二行输入N个数,表示各元素的值。
数据保证:
3 <= N <= 500000
-100 < Ai < 100
输出一个数,表示数组的两段区间(不相邻)之和的最大值。
Sample Input | Sample Output |
---|---|
6 2 3 -3 3 -2 4 |
10 |
对于样例,选择$2,3$和$3,-2,4$这两个区间,和为$10$.
PS.区间不相邻是指前一个区间的最后一个数和后一个区间的第一个数不相邻
Source
2016 UESTC Training for Dynamic Programming
最大区间和推广、预处理、打表、枚举间隔点
//本来实验过先找到一个最大区间,然后得到ans 和左右边界, 在里面找到最小符区间然后减去, 但这样显然不对,没办法^_^
从左到右扫一遍,从右到做扫一遍,处理好前缀和suml[maxn] 和 sumr[maxn],
并分别 O(n)的预处理出 1 ~ i 的最大连续区间和L[i], 和 i ~ n-1 的最大连续区间和 R[i]
然后枚举间隔点, i = 1 ~ n-2; ans = max(ans, L[i-1] + R[i+1];
复杂度O(n);
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 500000 + 8; int val[maxn], suml[maxn], sumr[maxn], L[maxn], R[maxn]; /* 5 -1 -1 -1 -1 0 */ int main() { #ifdef LOCAL freopen("a.txt", "r", stdin); #endif // LOCAL int N, maxs = 0; memset(val, 0, sizeof val); scanf("%d", &N); for(int i = 0; i < N; i++){ scanf("%d", &val[i]); } maxs = val[0]; int last = 0; //!last 要从0开始 for(int i = 0 ; i < N ; i++){ last = max(0,last)+val[i]; maxs = max(maxs,last); L[i] = maxs;//cout<<L[i]<<" "; } maxs = val[N-1]; last = 0; //!last 要从0开始 for(int i = N-1 ; i >= 0 ; i--){ last = max(0,last)+val[i]; maxs = max(maxs,last); R[i] = maxs;//cout<<L[i]<<" "; } int ans = -0x3f3f3f3f;//!!!!!!选两个区间的最大连续和,可能还是一个很小的负数 for(int i = 1; i+1 < N; i++){ ans= max(ans, L[i-1]+R[i+1]); } printf("%d", ans); return 0; } /* #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 500000 + 8; int val[maxn], sum[maxn]; int minsum(int x, int y) { int v, L, R, mins; if(y-x==1) return val[x]; int m = x + (y-x)/2; mins = min(minsum(x, m), minsum(m,y)); v = 0; L = val[m-1]; for(int i = m-1; i >= x; i--) L = min(L, v += val[i]); v = 0; R = val[m]; for(int i = m; i < y; i++) R = min(R, v += val[i]); return min(mins, L+R); } /* int maxsum(int x, int y) { int v, L, R, maxs; if(y-x==1) return val[x]; int m = x + (y-x)/2; maxs = max(maxsum(x, m), maxsum(m,y)); v = 0; L = val[m-1]; for(int i = m-1; i >= x; i--) L = max(L, v += val[i]); v = 0; R = val[m]; for(int i = m; i < y; i++) R = max(R, v += val[i]); return max(maxs, L+R); } // int main() { #ifdef LOCAL freopen("a.txt", "r", stdin); #endif // LOCAL int N, maxs = 0, maxi , mini = 0; memset(val, 0, sizeof val); scanf("%d", &N); for(int i = 0; i < N; i++){ scanf("%d", &val[i]); if(i!= 0) sum[i] = sum[i-1] + val[i]; else sum[i] = val[i]; } int maxs=val[0]; int mins=sum[0]; //保存当前遇到的最小S,当mins=S[0]或其他的,都不对。 for(int j=1;j<N;j++) { if(maxs <= sum[j]-mins){ maxs = sum[j]-mins; maxi = j; } if(mins > sum[j] && j < maxi){ mins = sum[j]; mini = j+1;//cout<<"mini=="<<endl; } } if(maxs < sum[N-1]){ maxs=sum[N-1]; maxi = N-1; } printf("mins=%d\n",mins); printf("%d\n",maxs); cout<<mini<<" "<<maxi+1<<endl; printf("%d", maxs-minsum(mini, maxi+1)); return 0; } */ /* #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 500000 + 8; int sum[maxn]; int main() { #ifdef LOCAL freopen("a.txt", "r", stdin); #endif // LOCAL int N, val, maxs, maxi,mins, mini; memset(sum, 0, sizeof sum); scanf("%d", &N); for(int i = 0; i < N; i++){ scanf("%d", &val); if(i!= 0) sum[i] = sum[i-1] + val; else sum[i] = val; } maxs=sum[N-1], maxi=N-1,mins=sum[N-1], mini=N-1; for(int i = N-1; i >= 0; i--){ maxs = max(maxs, sum[i]); mins = min(mins, sum[i]); } printf("%d", maxs- (mins <= sum[N-1] - maxs ? mins : sum[N-1] - maxs)); return 0; } */
------from ProLights