双子序列最大和
题目描述
给定一个长度为 n n n 的整数序列,要求从中选出两个连续子序列,使得这两个连续子序列的序列和之和最大,最终只需输出最大和。一个连续子序列的和为该子序列中所有数之和。每个连续子序列的最小长度为 1 1 1,并且两个连续子序列之间至少间隔一个数。
输入格式
第一行是一个整数表示 n n n。
第二行是 n n n 个整数表示整数序列。
输出格式
一个数,两个连续子序列的序列和之和。
样例 #1
样例输入 #1
5
83 223 -13 1331 -935
样例输出 #1
1637
样例 #2
样例输入 #2
3
83 223 -13
样例输出 #2
70
提示
对于 30 % 30\% 30% 的数据 N ≤ 100 N\le 100 N≤100。
对于 60 % 60\% 60% 的数据有 N ≤ 10000 N\le 10000 N≤10000。
对于 100 % 100\% 100% 的数据有 N ≤ 1000000 N\le 1000000 N≤1000000。
数据保证运算过程不会超过 long long
(int64
)。
思路
这道题相比于最大子段和,多了一个子串。那么我们可以类似一个字串来做:
设
f
i
为所有以
i
结尾的最大子串和集合(
1
到
i
)。
设f_i为所有以i结尾的最大子串和集合(1到i)。
设fi为所有以i结尾的最大子串和集合(1到i)。
设
g
i
为所有以
i
结尾的最大子串和集合(
n
到
i
)。
设g_i为所有以i结尾的最大子串和集合(n到i)。
设gi为所有以i结尾的最大子串和集合(n到i)。
- 其中 f f f 的状态转移为: f i = m a x ( f i − 1 + w i , w i ) f_i=max(f_{i-1}+w_i,w_i) fi=max(fi−1+wi,wi) ,这个什么意思呢?就是要么接在上一个串的末尾,要么就另起一个。 g g g 数组的状态转移为: g i = m a x ( g i − 1 + w i , w i ) g_i=max(g_{i-1}+w_i,w_i) gi=max(gi−1+wi,wi),这样最后我们在分别 max \max max 二者的相邻项,也就是 f i = m a x ( f i − 1 , f i ) f_i=max(f_{i-1},f_i) fi=max(fi−1,fi) 。
这样的话,我们可以对 f i f_i fi 从小到大枚举, g i g_i gi 则从大到小枚举,最后的答案是 m a x ( a n s , f i − 1 + g i + 1 ) max(ans,f_{i-1}+g_{i+1}) max(ans,fi−1+gi+1) ,为什么呢?因为题目说中间至少隔着一个。那么本道题的代码就很好写了。
AC 代码
#include<iostream>
#include<cstring>
using namespace std;
const int N = 2e6+10;
int w[N],n;
int f[N],g[N];
int ans=-2e9;
int main(){
cin>>n;
memset(f,-0x3f,sizeof f);
memset(g,-0x3f,sizeof g);
for(int i=1;i<=n;i++){
cin>>w[i];
}
for(int i=1;i<=n;i++){
f[i]=max(f[i-1]+w[i],w[i]);
}
for(int i=n;i;i--){
g[i]=max(g[i+1]+w[i],w[i]);
}
for(int i=1;i<=n;i++){
f[i]=max(f[i],f[i-1]);
}
for(int i=n;i;i--){
g[i]=max(g[i],g[i+1]);
}
for(int i=2;i<=n;i++){
ans=max(ans,f[i-1]+g[i+1]);
}
cout<<ans;
return 0;
}