最大子段和

GitHub跳转原题关系图返回列表

用一维动态规划维护以每个位置结尾的最大子段和,最后取所有状态的最大值。

OJ: luogu

题目 ID: P1115

难度:普及-

标签:dp动态规划python

日期: 2026-06-19 11:07

题意

给出一个整数序列,要求在所有连续且非空的子段中,找出和最大的那一段,并输出这个最大和。

思路

最直接的教学版做法是枚举所有连续子段:

cpp
// brute.cpp:枚举所有连续子段求和,作为教学版和对拍基准程序。
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 205;

int n;
int a[MAXN];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    long long ans = -(1LL << 60);
    for (int l = 1; l <= n; l++) {
        long long sum = 0;
        for (int r = l; r <= n; r++) {
            sum += a[r];
            ans = max(ans, sum);
        }
    }

    cout << ans << '\n';
    return 0;
}

但正式解法只要线性 DP。

dp[i] 表示以第 i 个元素结尾的最大子段和。

那么以 i 结尾的最优子段只有两种选择:

  1. 只取 a[i]
  2. a[i] 接到前一个最优子段后面

因此转移是:

dp[i] = max(a[i], dp[i-1] + a[i])

最后对所有 dp[i] 取最大值就是答案。

DP 公式

dpidp_i 表示以第 ii 个数结尾的最大连续子段和。初始化:

dp1=a1 dp_1=a_1

i>1i>1 有:

dpi=max(ai, dpi1+ai) dp_i=\max(a_i,\ dp_{i-1}+a_i)

最终答案不是固定结尾,而是:

max1indpi \max_{1\leqslant i\leqslant n} dp_i

样例 [2,-4,3,-1,2,-4,3] 的状态如下:

a[i] 2 -4 3 -1 2 -4 3
dp[i] 2 -2 3 2 4 0 3
当前答案 2 2 3 3 4 4 4

每格只依赖前一格,所以 Python 正解把整张 dp 表压缩成变量 current

公式解释:以 i 结尾的最优子段只有两种情况:从 i 重新开始,或者把 a_i 接到前一个结尾最优子段后面。对每个结尾算出最优值后,全局答案就是所有结尾里的最大值。

Python 知识

  • 整数迭代器直接流过输入,不需要保存数组或 dp 列表。
  • current = max(value, current + value) 与状态转移公式完全对应。
  • 首项单独初始化,保证全负序列也必须选择非空子段。

代码

python
import sys


data = iter(map(int, sys.stdin.buffer.read().split()))
_ = next(data)
current = answer = next(data)
for value in data:
    current = max(value, current + value)
    answer = max(answer, current)
print(answer)

复杂度

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(1)O(1)(不计输入缓冲)

总结

这题是最大子段和的经典模型。

关键在于把“全局最优子段”转成“以某位置结尾的最优子段”来做状态转移。

一图流解析

这张图把本题的建模、关键转移、实现检查和训练方法压缩到一页,适合读完正文后复盘。

一图流解析