A 最大连续子序列
- 本题是动态规划的经典题目,只需要 O(n) 复杂度。
- 一般的最大连续子序列问题只需要求出最大和,求法如下:步骤一、令状态 dp[i] 表示以 N[i] 作为末尾的连续序列的最大和(这里是说 N[i] 必须作为连续序列的末尾)。通过设置这么一个 dp 数组,要求的最大和其实就是 dp[0]、dp[1]、……dp[n-1] 中的最大值(因为到底以哪个元素结尾未知)步骤二、因为 dp[i] 要求是必须以 N[i] 结尾的连续序列,那么只有两种情况:①这个最大和的连续序列只有一个元素,即以 N[i] 开始,以 N[i] 结尾;②这个最大和的连续序列有多个元素,即从前面的某处 N[p] 开始(p < i),一直到 N[i] 结尾。对第一种情况,最大和就是 N[i] 本身,对第二种情况,最大和是 dp[i-1]+N[i],即 N[p]+……N[i-1]+N[i]。由于只有这两种情况,于是得到状态转移方程:dp[i] = max { N[i], dp[i-1]+N[i] },这个式子只和 i 与 i 之前的元素有关,且边界为 dp[0]=N[0] ,由此从小到大枚举 i,即可得到整个 dp 数组。
- 本题需要求出最大连续子序列中的第一个和最后一个元素,因此在更新状态的时候需要记录第一个元素和最后一个元素,这里设置 dp 为一个结构体数组,结点中记录当前最大和以及第一个元素和最后一个元素,在更新状态时需要将结构体内部数据更新。最后遍历 dp 输出最大和。参考代码如下。
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 10010;
int N[MAXN];
struct node { int sum, first, last; }dp[MAXN];
int main() {
int k;
while (scanf("%d", &k) && k) {
for (int i = 0; i < k; i++)
scanf("%d", &N[i]);
dp[0].sum = dp[0].first = dp[0].last = N[0];
for (int i = 1; i < k; i++)
if (N[i] < dp[i - 1].sum + N[i]) {
dp[i].sum = dp[i - 1].sum + N[i];
dp[i].first = dp[i - 1].first;
dp[i].last = N[i];
}
else dp[i].sum = dp[i].first = dp[i].last = N[i];
int index = 0;
for (int i = 0; i < k; i++)
if (dp[i].sum > dp[index].sum)
index = i;
if (dp[index].sum < 0) printf("0 %d %d\n", N[0], N[k - 1]);
else printf("%d %d %d\n", dp[index].sum, dp[index].first, dp[index].last);
}
return 0;
}