51nod 1254 最大子段和 V2

N个整数组成的序列a[1],a[2],a[3],…,a[n],你可以对数组中的一对元素进行交换,并且交换后求a[1]至a[n]的最大子段和,所能得到的结果是所有交换中最大的。当所给的整数均为负数时和为0。 例如:{-2,11,-4,13,-5,-2, 4}将 -4 和 4 交换,{-2,11,4,13,-5,-2, -4},最大子段和为11 + 4 + 13 = 28。  

输入

第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N + 1行:N个整数(-10^9 <= A[i] <= 10^9)

输出

输出交换一次后的最大子段和。

输入样例

7
-2
11
-4
13
-5
-2
4

输出样例

28

这里用前缀和,对于每一个区间[l,r]的和为sum[r] - sum[l - 1] - min[l,r] + max(max[l - 1],max[r + 1]),其中max(max[l - 1],max[r + 1])可以先打表,只要知道了l和r,就可以得出,sum[r] - min[l,r],也比较好求,显然sum[r]应该越大越好,那么我们可以枚举l,保证sum[r],足够大,且sum[r] - min[l,r]足够大,计算sum[r] - min[l,r],只需要用遍历到当前最大的sum,找到最大的sum - s[i]即可。
代码:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
///formula : sum[r] - sum[l - 1] - min[l,r] + max(max[1,l - 1],max[r + 1,n])
int n;
ll sum[50005];
int s[50005];
int lmax[50005],rmax[50005];
int main() {
    while(~scanf("%d",&n)) {
        for(int i = 1;i <= n;i ++) {
            scanf("%d",&s[i]);
            sum[i] = sum[i - 1] + s[i];
        }
        for(int i = 0;i < n;i ++) {
            lmax[i + 1] = max(lmax[i],s[i + 1]);
            rmax[n - i] = max(rmax[n - i + 1],s[n - i]);
        }
        int maxi = n;
        ll sumr_min,ans = 0;
        for(int i = n;i >= 1;i --) {
            if(sum[i] >= sum[maxi]) {
                maxi = i;
                sumr_min = sum[i] - s[i];
            }
            sumr_min = max(sumr_min,sum[maxi] - s[i]);
            ans = max(ans,sumr_min - sum[i - 1] + max(lmax[i - 1],rmax[maxi + 1]));
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

上一篇:原根 51Nod - 1135


下一篇:51nod 1720 祖玛