1007 Maximum Subsequence Sum (25分)
题目
求序列的最大连续子序列和。序列长度不超过 1e4。
分析
经典 DP 问题,最大子段和。
可以很简单想到令
d
p
[
i
]
dp[i]
dp[i] 表示为 0~i 序列的最大子段和,对于每个元素只有三种选择:
① 不加入当前元素
② 加入当前元素,且与前面并一起
③ 加入当前元素,用当前元素重新开始算子段
d
p
[
i
]
=
m
a
x
(
d
p
[
i
−
1
]
,
d
p
[
i
−
1
]
+
a
[
i
]
,
a
[
i
]
)
dp[i] = max(dp[i-1], dp[i-1]+a[i], a[i])
dp[i]=max(dp[i−1],dp[i−1]+a[i],a[i])。结果就是
d
p
[
n
]
dp[n]
dp[n]。
可以看出状态转移比较麻烦,而且不好维护当前最大子段和的头尾。
但如果将 d p [ i ] dp[i] dp[i] 表示为 0~i 序列以 a [ i ] a[i] a[i] 结尾的最大子段和,转移方程就变为: d p [ i ] = m a x ( a [ i ] , d p [ i − 1 ] + a [ i ] ) dp[i] = max(a[i], dp[i-1]+a[i]) dp[i]=max(a[i],dp[i−1]+a[i]),只需要维和头即可。但是结果变为了所有 d p [ i ] dp[i] dp[i] 的最大值。
#include <bits/stdc++.h>
using namespace std;
#define db(x) cout<<x<<endl
typedef long long ll;
const int INF = 0x3f3f3f;
const int N = 1e5 + 10;
const ll mod = 2147483648;
int n, tmp = 1, a[N]; // tmp 代表当前最大子段和的起点
int dp[N]; // dp[i] 表示以 i 结尾的最大子段和的值,
int main() {
scanf("%d", &n);
int maxn = -INF, res_s, res_e;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1, x; i <= n; i++) {
if (i == 1) dp[i] = a[i];
if (a[i] >= dp[i - 1] + a[i]) {
tmp = i; dp[i] = a[i];
} else {
dp[i] = dp[i - 1] + a[i];
}
if (dp[i] > maxn) { // 每次更新 maxn
maxn = dp[i];
res_s = tmp; res_e = i;
}
}
if (maxn < 0) printf("%d %d %d\n", 0, a[1], a[n]);
else printf("%d %d %d\n", maxn, a[res_s], a[res_e]);
return 0;
}