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 题解的主要内容,如果未能解决你的问题,请参考以下文章

51nod 1254 最大子段和 V2

51nod1254 最大子段和 V2

51nod 1052 最大M子段和 & 1053 最大M子段和 V2

51nod 1053 最大M子段和 V2

51Nod1053 最大M子段和V2 二分+DP

51NOD-01049 最大子段和