Welcome to LHY's Blog!

DP

1. 最大的和

来源《信息学奥赛一本通》
原题链接

题目描述

对于给定的整数序列 $A=\lbrace a_1,a_2,…,a_n \rbrace$,找出两个不重合连续子段,使得两子段中所有数字的和最大。

我们如下定义函数 $d(A)$:

$$d(A) = max_{1≤s_1≤t_1<s_2≤t_2≤n} \lbrace \sum^{t_1}_{i=s_1}a_i+\sum^{t_2}_{j=s_2}a_j \rbrace$$

我们的目标就是求出 $d(A)$。

输入格式

第一行是一个整数 $T$,代表一共有多少组数据。

接下来是 $T$ 组数据。

每组数据的第一行是一个整数,代表数据个数据 $n$,第二行是 $n$ 个整数 $a_1,a_2,…,a_n$。

输出格式

每组数据输出一个整数,占一行,就是 $d(A)$ 的值。

数据范围

$1 \le T \le 30$,
$2 \le n \le 50000$,
$|a_i| \le 10000$

输入样例

10
1 -1 2 2 3 -3 4 -4 5 -5

输出样例

13

算法:(线性dp + 前后缀分解)\(O(n)\)

算法内容

  • 首先我们先回忆最大连续子序列和问题是如何解决的:
    状态表示:
    \(\qquad\)集合:\(f[i]\)表示以\(1\)~\(i\)中以\(i\)结尾的所有连续子序列
    \(\qquad\)属性:最大值
    状态计算:
    \(\qquad\)集合划分:分为两类:第一类是只包含\(i\),第二类是不只包含\(i\)
    \(\qquad\)转移方程:\(f[i] = max(f[i - 1] + w[i], w[i])\),即 \(f[i] = max(f[i - 1], 0) + w[i]\),优化空间后为\(s = max(s, 0) + w[i]\)

  • 前后缀分解技巧:
    本题可采取用\(i\)来枚举两段区间的分界线,预处理\(g[i]\)为以i左侧的最大连续子序列和,\(h[i]\)为i右侧的最大连续子序列和,此时以\(i\)为分界线的满足题意的答案就是\(res = g[i] + h[i + 1]\),我们在遍历所有分界线时只需要对\(res\)\(max\)即可得到最终答案

C++代码

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 50010, INF = 1e9;

int n;
int w[N], g[N], h[N];

int main()
{
    int T;
    scanf("%d", &T);

    while (T -- )
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);

        g[0] = -INF;
        for (int i = 1, s = -INF; i <= n; i ++ )
        {
            s = max(s, 0) + w[i];
            g[i] = max(g[i - 1], s);
        }

        h[n + 1] = -INF;
        for (int i = n, s = -INF; i; i -- )
        {
            s = max(s, 0) + w[i];
            h[i] = max(h[i + 1], s);
        }

        int res = -INF;
        for (int i = 1; i <= n; i ++ )
            res = max(res, g[i] + h[i + 1]);

        printf("%d\n", res);
    }

    return 0;
}

posted @ 2023-04-07 17:23  LiHaoyu  阅读(91)  评论(0)    收藏  举报