51Nod-1254 最大子段和V2 题解
Posted kuangbiaopilihu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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)个整数((-10^9 <= A[i] <= 10^9))
输出格式
输出交换一次后的最大子段和。
输入样例
7
-2 11 -4 13 -5 -2 4
输出样例
28
分析
首先很容易想要一个错误的解法就是,先找到一个最大子段和,然后从其他地方找一个最大值,跟里面的最小值交换,反例比较容易想到:(-9, 5, 1, 7, -10, 5, 4),最大子段和为 ({5, 1, 7}),但是用 (4) 换 (-10) 结果才是最优的。- 我们可以从交换的数入手,如果某个数与 (a[i]) 交换后,最优解为包含新 (a[i]) 的最大子段和。那么这个子段和的构成就是 (新的a[i]+a[i-1]向左延伸的最大子段和+a[i+1] 向右延伸的最大子段和)
- 那么这个用来与 (a[i]) 交换的值怎么找呢?很显然只能从“向左/右延伸的区间”和“延伸以外的区间”两种区域来找一个最大值(肯定是要拿最大值换子段中的最小值)。
- 如果从“向左/右延伸的区间”来找最大值,假设我们向右延伸的右边界为 (r),其实最终得到的和等于以 (r) 为结尾的最大子段和,这样肯定不会比从“延伸以外的区间”找个最大值(如果能找到,则交换,如果找不到就不换)更优。
- 因此,就只能从“延伸以外的区间”找一个最大值(如果能找到)来与 (a[i]) 交换。
- 借助以上的思路,我们需要维护以某个点为结尾/开始的最大子段和,及延伸到的左/右边界,DP一下即可,例如求以 (a[i]) 结尾的最大子段和:(leftmaxsubsum[i] = max(leftmaxsubsum[i-1]+a[i], 0))。以 (a[i]) 开头的同理。
- 因为还需要找最值,所以还需要维护以某个点为结尾/开始的最大值 leftmax[i] 和 rightmax[i]。
- 我们枚举每个被替换的数 (a[i]),那么答案就是:[max_{i=1}^{n}(leftmaxsubsum[i-1]+rightmaxsubsum[i+1]+max(leftmax[i-2], rightmax[i+2])) ]
注意边界问题的处理,不要出现负的下标等等
// 求第i个数向左延伸的最大子段和(向右的一样)
leftmaxsubsum[0] = 0;
lpos[0] = 0; // 记录向左延伸到的位置
for (int i = 1; i <= n; ++i) {
if (leftmaxsubsum[i - 1] + a[i] > 0) {
leftmaxsubsum[i] = leftmaxsubsum[i - 1] + a[i];
lpos[i] = lpos[i - 1]; // 如果直接拼接到前面,延伸到位置就是前面一个数延伸到的位置
} else {
leftmaxsubsum[i] = 0;
lpos[i] = 0; //如果一个数也不能延伸,相当于一个空字段,记为0
}
if (a[i] > 0 && lpos[i] == 0) lpos[i] = i; // 有可能前面的数不能延伸,但是a[i]大于0,那么相当于该子段只有它自己
}
// 枚举每个被替换的点
ll ans = 0, tmp;
ll l, r;
for (int i = 1; i <= n; ++i) {
// 先找到延伸到的边界
if (lpos[i - 1] == 0) { // 不能延伸的情况
l = leftmax[i - 1];
} else { // 能延伸的情况
l = leftmax[lpos[i - 1] - 1];
}
if (rpos[i + 1] == 0) {
r = rightmax[i + 1];
} else {
r = rightmax[rpos[i + 1] + 1];
}
tmp = leftmaxsubsum[i - 1] + rightmaxsubsum[i + 1] + max(l, r);
ans = max(ans, tmp);
}
以上是关于51Nod-1254 最大子段和V2 题解的主要内容,如果未能解决你的问题,请参考以下文章