51nod1524 最大子段和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。
Input
第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N + 1行:N个整数(-10^9 <= A[i] <= 10^9)
Output
输出交换一次后的最大子段和。
Sample Input
7
-2
11
-4
13
-5
-2
4
Sample Output
28
一点题外话
这道题真的很不起眼qwq
一开始做的时候各位大佬都是各显神通,然鹅结果不如人意,然后去网上找题解,结果题解里的显然显然不是那么正确...(https://www.cnblogs.com/hbhszxyb/p/13130945.html)。
能AC完全是数据水,于是狠狠地暴露了一波,还好咱当时就是没看懂那个显然就没抄那个代码qwq(然后就恬不知耻地抄了另一个)。
最后还是听虎哥讲听明白了,难受
开始整活
思路来自:https://blog.csdn.net/zlh_hhhh/article/details/78176629 (膜拜大佬)
首先,我们交换有三种情况,一是交换的两个数都在最优子段内,二是交换的两个数都不在最优子段内,三是一个在最优子段内一个不在
显然,前两种对我们的答案没有什么影响,我们需要处理的就是第三种。
虎哥说:“如果一道题怎么都没有思路,那么就把它当成DP来做”
于是,我们先不考虑复杂度的问题,从DP的方向来想。
我们先从左到右遍历,遍历到i,我们假设把左边的某一个数a[k]与a[i]替换。
然后定义两个式子,1为此时新的a[i]向左延伸能得到的最大子段和,2为a[i+1]向右延伸能得到的最大子段和,那么显然我们的答案就是1式加2式,然后不断往下遍历取最优。
2式很简单,我们可以通过预处理直接得到,问题在于1式,这里就要用DP来考虑一下。
我们记遍历到第i位时的1式为G[i]。
那么我们的思路就是跟着定义走,枚举到i,然后从1往右枚举k,交换,然后判断此时向左延伸的最大子段和,取最优,式子写出来是:
\(G[i]=\max_{k=1}^{i-2}\big( A[k]+\max_{t=k+1}^{i-1}(\sum_{j=t}^{i-1}A[j])\big)\)
可能有神犇注意到这个式子并不完全,因为我们只枚举到了i-2,为什么不能枚举到i-1呢?
很显然,枚举到i-2,意味着我们向左延伸的最大子段和的长度至少为2。如果长度为1,那么此时
\(\max_{t=k+1}^{i-1}(\sum_{j=t}^{i-1}A[j])\) 显然是不执行的。
所以我们的式子只剩下: \(G[i]=\max_{k=1}^{i-1}\big( A[k])\)
而k此时等于i-1,所以实际上我们的选择被固定在a[i-1]这一个数上,所以此时长度为1的最大子段和就是a[i-1]。
而这显然不正确,如果长度大于等于2,因为考虑到子段的顺序问题,那么照上边那么做是没问题的,但我们此时长度为1,显然我们可以直接从左边找一个最大值过来换掉a[i],而不一定是a[i-1],所以我们需要特判一下长度为1的情况。
我们记:\(M[i-1]=\max_{k=1}^{i-1}A[k]\)
于是就有了:\(G[i]=max\Big(\max_{k=1}^{i-2}\big( A[k]+\max_{t=k+1}^{i-1}(\sum_{j=t}^{i-1}A[j])\big),M[i-1]\Big)\)
照猫画虎,就有了:\(G[i+1]=max\Big(\max_{k=1}^{i-1}\big( A[k]+\max_{t=k+1}^{i}(\sum_{j=t}^{i}A[j])\big),M[i]\Big)\)
我们试着改写一下:
\(G[i+1]=max\Big(\max_{k=1}^{i-1}\big( A[k]+\max_{t=k+1}^{i}(\sum_{j=t}^{i}A[j])\big),M[i]\Big )\)
\(=max\Big(\max_{k=1}^{i-2}\big( A[k]+\max_{t=k+1}^{i-1}(\sum_{j=t}^{i-1}A[j]) )+A[i],M[i]\Big )\)
这个改写其实是不正确的,因为我们知道\(\max_{k=1}^{i-2}\big( A[k]+\max_{t=k+1}^{i-1}(\sum_{j=t}^{i-1}A[j])\)其实是\(G[i]\)的长度至少为2的部分,在加上上边我们提出来这个a[i],就变成了G[i+1]长度至少为3的部分,而M[i]是长度为1的部分,显然我们缺少了长度为2的部分,那么解决也很简单,直接在判断里加上就好了。
所以,最后的结果是:
\(G[i+1]=max\Big(\max_{k=1}^{i-2}\big( A[k]+\max_{t=k+1}^{i-1}(\sum_{j=t}^{i-1}A[j]) )+A[i],M[i-1]+A[i],M[i]\Big)\)
即
\(G[i+1]=max(G[i]+A[i],M[i-1]+a[i],M[i])\)
递推式有了,代码就比较容易了,还有就是注意我们从左到右扫一边不能判断全部的情况,所以还要反着扫一边。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 50000 + 10;
#define ll long long
#define inf 0x3f3f3f3f
int a[maxn], maxx = -inf;
ll sl[maxn], sr[maxn], ans = -inf;
ll G1[maxn], G2[maxn];
int main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
for(int i = 1, j = n; i <= n; i++, j--){
sl[i] = sl[i-1] + a[i];
sr[j] = sr[j+1] + a[j]; // 保存以j为开始向右延伸的最大子段和
if(sl[i] < 0) sl[i] = 0;
if(sr[j] < 0) sr[j] = 0;
if(ans < sl[i]) ans = sl[i]; // 防止存在原始的最大子段和就是最优解的情况,这是存在的,因为我们可以选择交换组合内的两个数
}
maxx = a[1];
for(int i = 2; i <= n; i++){
G1[i] = G1[i-1] + a[i-1];
if(G1[i] < maxx) G1[i] = maxx;
if(ans < G1[i] + sr[i+1]) ans = G1[i] + sr[i+1];
if(maxx < a[i]) maxx = a[i];
}
maxx = a[n];
for(int i = n - 1; i > 0; i--){
G2[i] = G2[i+1] + a[i+1];
if(G2[i] < maxx) G2[i] = maxx;
if(ans < G2[i] + sl[i-1]) ans = G2[i] + sl[i-1];
if(maxx < a[i]) maxx = a[i];
}
printf("%lld\n", ans);
return 0;
}
51nod1524 最大子段和V2的更多相关文章
- 51nod 1053 最大M子段和 V2
N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的.如果M >= N个数中正数的个数,那么输出所有正数的和. 例如:-2 ...
- 最大M子段和 V2
51nod1053 这题还是我们熟悉的M子段和,只不过N,M<=50000. 这题似乎是一个堆+链表的题目啊 开始考虑把所有正数负数锁在一起. 比如: 1 2 3 -1 –2 -3 666 缩成 ...
- [51nod1254]最大子段和 V2
N个整数组成的序列a[1],a[2],a[3],-,a[n],你可以对数组中的一对元素进行交换,并且交换后求a[1]至a[n]的最大子段和,所能得到的结果是所有交换中最大的.当所给的整数均为负数时和为 ...
- 51nod 1254 最大子段和 V2 ——单调栈
N个整数组成的序列a[1],a[2],a[3],…,a[n],你可以对数组中的一对元素进行交换,并且交换后求a[1]至a[n]的最大子段和,所能得到的结果是所有交换中最大的.当所给的整数均为负数时和为 ...
- 51nod 1254 最大子段和 V2
N个整数组成的序列a[1],a[2],a[3],…,a[n],你可以对数组中的一对元素进行交换,并且交换后求a[1]至a[n]的最大子段和,所能得到的结果是所有交换中最大的.当所给的整数均为负数时和为 ...
- 51Nod1053 最大M子段和V2 二分+DP
传送门 直接DP的话最多也只能做到\(O(nm)\),对于\(5\times 10^4\)的数据范围实在无能为力 夹克老爷提供的做法是贪心,思想大概是在调整的同时,合理构造每个选择对应的新状态,使得新 ...
- 51nod1254 最大子段和 V2 DP
---题面--- 题解: 表示今天做题一点都不顺.... 这题也是看了题解思路然后自己想转移的. 看的题解其实不是这道题,但是是这道题的加强版,因为那道题允许交换k对数. 因为我们选出的是连续的一段, ...
- 51nod1053 最大M子段和 V2
$n \leq 50000$的序列,问选不超过$m \leq 50000$个区间使得和最大. 如果正数区间总数比$m$小那肯定全选.否则有两种方式减少区间数量:丢掉一个正区间:补一个负区间连接两个正区 ...
- 51Nod 最大M子段和系列 V1 V2 V3
前言 \(HE\)沾\(BJ\)的光成功滚回家里了...这堆最大子段和的题抠了半天,然而各位\(dalao\)们都已经去做概率了...先%为敬. 引流之主:老姚的博客 最大M子段和 V1 思路 最简单 ...
随机推荐
- XML基础(转)
一.XML是什么?作用是什么?1.XML是指可扩展标记语言(eXtensible Markup Language),用户自定义的标签.相对于HTML来讲的.2.XML被设计的宗旨是表示数据.HTML是 ...
- 第一章04-Activity中常用的标志位
Activity的LaunchMode Android中提供了四中Activity的启动模式 1. standard 2. singleTop 3. singleTask 4. signleInsta ...
- hadoop知识整理(1)之HDFS
一.HDFS是一个分布式文件系统 体系架构: hdfs主要包含了3部分,namenode.datanode和secondaryNameNode namenode主要作用和运行方式: 1)管理hdfs的 ...
- easypoi 读取 Excel 简单应用
背景 在做接口测试的时候,经常会使用 Excel 来存储对应的接口信息和用例信息,为了方便程序的读取,引入easypoi 工具来读取 Excel 内容.easypoi 比起 poi 使用更加的方便,代 ...
- 「雅礼集训 2017 Day4」洗衣服
题目 点这里看题目. 分析 首先考虑只有洗衣机的情况.我们可以想到,当前洗衣任务结束越早的洗衣机应该被先用,因此可以用堆来动态维护. 再考虑有烘干机的情况.很显然,越晚洗完的衣服应该越早烘 ...
- windows下Python版本切换使用方法
由于历史原因,Python有两个大的版本分支,Python2和Python3,又由于一些库只支持某个版本分支,所以需要在电脑上同时安装Python2和Python3,因此如何让两个版本的Python兼 ...
- 小师妹学JVM之:JVM的架构和执行过程
目录 简介 JVM是一种标准 java程序的执行顺序 JVM的架构 类加载系统 运行时数据区域 执行引擎 总结 简介 JVM也叫Java Virtual Machine,它是java程序运行的基础,负 ...
- Python变量和注释
1.变量与变量的作用: (1)什么是变量:变量源于数学,是计算机语言中能存储计算结果或能表示值抽象概念.变量可以通过变量名访问.在指令式语言中,变量通常是可变的:在Python中变量名必须是大小写英文 ...
- 超详细实战教程丨多场景解析如何迁移Rancher Server
本文转自Rancher Labs 作者介绍 王海龙,Rancher中国社区技术经理,负责Rancher中国技术社区的维护和运营.拥有6年的云计算领域经验,经历了OpenStack到Kubernetes ...
- TensorFlow从0到1之TensorFlow csv文件读取数据(14)
大多数人了解 Pandas 及其在处理大数据文件方面的实用性.TensorFlow 提供了读取这种文件的方法. 前面章节中,介绍了如何在 TensorFlow 中读取文件,本节将重点介绍如何从 CSV ...