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; }
原文地址:https://www.cnblogs.com/8023spz/p/10914391.html
时间: 2024-10-08 20:29:51