引言
许多实际问题涉及一个连续区间的数量性质。比如国际期货市场内某种商品在某个月的第1,2,…,31天内的价格涨幅分别记为a1,a2,...,a31a_1 ,a_2,...,a_{31}a1,a2,...,a31。若某天价格下降,这天的涨幅就是负值。我们想知道在哪些连续的天内该商品价格具有最高涨幅,究竟涨了多少。这个问题可抽象为下述子序列和问题。
设A=<a1,a2,...,a31>A=<a_1 ,a_2,...,a_{31}>A=<a1,a2,...,a31>是nnn个整数的序列,称<ai,...,aj><a_i,...,a_j><ai,...,aj>为该序列的子序列,其中1≤i≤j≤n1\le i \le j \le n1≤i≤j≤n。子序列的元素和∑k=1j\sum\limits_{k=1}^{j}k=1∑j称为A的子序列和。
问题描述
数组int a[] = {3, -5, 2, 4, -1, 6}
;某几个连续的子序列其和最大,比如:a[0] + a[1] = -2, a[2] + a[3] + a[4] + a[5] = 11
。 则a[2]
,a[3]
,a[4]
,a[5]
组成的子序列即为所求。
解题思路
因为必须连续,所以计算顺序从第一个或最后一个元素开始扫描。
- (1) 层(阶段、步)的划分
一个元素对应一个层,多少个元素就多少层(依据:便于最优解的描述)。 - (2) 最优解递归方程
采用从左向右(从前向后)的顺序对数组a进行计算。设b[i]表示第i处,以a[i]结尾的子序列的最大和,则
b[i]=max( a[i]+b[i-1], a[i] ) - (3) 最终的解
b数组元素最大值。
样例
输入样例:
6
3 -5 2 4 -1 6
输出样例:
sum = 11
list: 2,3,4,5
实例代码
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
struct DATA{
int value;
int maxVal;
vector<int> lists;
};
int main() {
vector<DATA> datas;
ifstream in("data.txt");
int value = 0;
while(in >> value) {
DATA data;
data.value = value;
data.maxVal = value;
datas.push_back(data);
}
datas[0].lists.push_back(0);
for(int i = 1; i < datas.size(); ++i) {
int tmp = datas[i].value + datas[i - 1].maxVal;
if(datas[i].value <= tmp) {
datas[i].lists = datas[i - 1].lists;
datas[i].maxVal = tmp;
}
datas[i].lists.push_back(i);
}
DATA md;
md.value = datas[0].value;
md.maxVal = datas[0].maxVal;
md.lists = datas[0].lists;
int maxval = datas[0].maxVal;
for(DATA ds : datas) {
if (ds.maxVal > maxval) {
md.value = ds.value;
md.maxVal = ds.maxVal;
md.lists = ds.lists;
}
}
cout << "sum = " << md.maxVal << endl;
cout << "list: ";
for (int d : md.lists) {
cout << d << ",";
}
cout << endl;
return 0;
}
测试结果
输入:
6
3 -5 2 4 -1 6
输出结果: