动态规划之最大两段连续子序列和

写在前面:本题是聊天时从一位朋友那里所得,暂时没有找到题目出处与标解。博主自己用动态规划与一点小优化做出了本题,可能并不是最优解,欢迎指教。

题目描述

本题是最大连续子序列和的加强版:

最大连续子序列和问题_AryCra_07的博客-CSDN博客

给定一个数字序列A_1,A_2,...,A_n,求两段互不相交的连续子序列,使得两段子序列的和最大,输出这个最大和。

题目分析

两次DP

由于题目要求我们找两段互不相交的子序列,那必然一个在左,一个在右。那么,我们可以采用“two pointers”的思想(简单说来就是从两头遍历),化归成处理两个单段最大连续子序列的问题。

具体一点来说,基本思路如下:

  1. 处理单段子序列问题的时候,我们用dp记录以该点为结尾的最大连续子序列;
  2. 那么对于本题,我们可以定义两个dp数组dp_left,dp_right,从两头开始动态规划,dp_left记录左序列以该点结尾(从左到右数)时的最大子序列,dp_right记录右序列以该点结尾(从右往左数)的最大子序列;
  3. 完成之后,我们最后的操作是遍历每个点。对于每个点,我们要找到在它左边的最大子序列与在它右边的最大子序列(因为两个序列不能相交嘛,就以一个点为分界线来找),并求出基于这个点找到的最大左右序列之和,进而求出所有情况下的最大和。

一个优化

问题在与第3步的寻找操作。我们总不能每遍历到一个点,都去寻找一遍基于这个点的最大左序列和与最大右序列和。诚然这又可以用dp解决,但我们这里有一个很棒的优化方法。代码里见。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int dp_leftSequence[N], dp_rightSequence[N], sequence[N];
int main()
{
    ios::sync_with_stdio(false); //关闭流同步,提高cin速度
    int n;
    cin >> n;
    for(int i = 1; i <= n; i ++)
        cin >> sequence[i];
    
    dp_leftSequence[1] = sequence[1];
    dp_rightSequence[n] = sequence[n]; //赋边界条件(初始状态)

    for(int i = 1; i <= n; i ++)
        dp_leftSequence[i] = max(sequence[i], dp_leftSequence[i - 1] + sequence[i]);
        //状态转移方程
    for(int i = 2; i <= n; i ++) //important optmization,简化寻找操作
        if(dp_leftSequence[i] < dp_leftSequence[i - 1])
            dp_leftSequence[i] = dp_leftSequence[i - 1];
    
    for(int i = n; i >= 1; i --)
        dp_rightSequence[i] = max(sequence[i], dp_rightSequence[i + 1] + sequence[i]);
        //状态转移方程
    for(int i = n - 1; i >= 1; i --) //important optmization,简化寻找操作
        if(dp_rightSequence[i] < dp_rightSequence[i + 1])
            dp_rightSequence[i] = dp_rightSequence[i + 1];

    int tmp = 0, maxn = -1;

    for(int i = 1; i <= n-1; i ++) { //遍历每个点
        tmp = dp_leftSequence[i] + dp_rightSequence[i + 1];
        maxn = max(tmp, maxn);
    }
    cout << maxn << '\n';

    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AryCra_07

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值