P1115. 最大子段和

考点

  • 分治
  • 线性动态规划

题解

见思路

思路

线性动态规划

dp[i]的含义为:

a[i]为结尾的连续序列里,连续序列和的极大值

显然能得到状态转移方程: \[ dp\left[ i \right] =\max \left( dp\left[ i-1 \right] +a\left[ i \right] ,a\left[ i \right] \right) ,2\leqslant i\leqslant n \] 直接编程即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 50;
int n, ans, a[maxn], dp[maxn];

int main() {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
ans = dp[1] = a[1];
for (int i = 2; i <= n; ++i) {
dp[i] = max(dp[i - 1] + a[i], a[i]);
ans = max(ans, dp[i]);
}
cout << ans;
return 0;
}

分治

考虑最大子段和出现的位置:

  1. 只出现在左半部分,不包含中线;
  2. 只出现在右半部分,不包含中线;
  3. 包含中线。

前两种可能分治处理即可,重点是第三种可能。

由于一定要经过中线,可以从中线开始向两边走;

分别取左半部分的最大后缀和右半部分的最大前缀后相加即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 50, mi = 0xc0c0c0c0;
int n, ans = mi, a[maxn];

void f(int l, int r) {
if (l == r) {
ans = max(ans, a[l]);
return;
}
int mid = r + ((l - r) >> 1);
int lsm = 0, rsm = 0, lmx = mi, rmx = mi;
for (int i = mid; i >= l; --i) lsm += a[i], lmx = max(lmx, lsm);
for (int i = mid + 1; i <= r; ++i) rsm += a[i], rmx = max(rmx, rsm);
ans = max(ans, lmx + rmx);
f(l, mid), f(mid + 1, r);
}

int main() {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
f(1, n);
cout << ans;
return 0;
}