码迷,mamicode.com
首页 > 其他好文 > 详细

51Nod-1254 最大子段和V2 题解

时间:2020-06-12 17:30:45      阅读:92      评论:0      收藏:0      [点我收藏+]

标签:最优   最大子段和   题解   line   注意   输出   math   描述   整数   

题目描述

\(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 题解

标签:最优   最大子段和   题解   line   注意   输出   math   描述   整数   

原文地址:https://www.cnblogs.com/kuangbiaopilihu/p/13097749.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!