一、概述
1. 问题描述
有n个节点,下标1~n,每个节点有权值,记做v[i],现需要找到一个递增下标序列 ,
其中 ,使得该序列对应的权值之和最大。
2. 问题链接
HDU -- Super Jumping! Jumping! Jumping!(ACM Step: 3.2.3)
3. 问题截图
图1.1 问题截图
二、算法思路
首先分析问题,问题所求可以用下述公式表示:
公式等价于下式(将的范围提出括号):
单独看括号内的公式时,它表示:当是解序列的最后一个节点时,满足条件的节点(同时满足节点序号递增、以及节点权值递增条件的节点)的最大权值和。将此公式记为,即有下式:
那么之间有什么关系呢?
当时,,表示当节点1,作为解序列的最后一个节点,此时满足条件的只有一个节点,即此时,
当时,,和有没有关系呢?可以想象,,是节点1作为解序列的最后一个节点的最大权值和,如果此时有条件成立,那么节点2可以用节点1时的最大权值和为基础,加上自己的权值作为最大权值和,即;若,此时节点2不能用到节点1的结果,此时。
当时,同样的,此时,节点1和节点2作为解序列的最后节点的最大权值和都已经算出,节点3可以分别取和两个节点的权值比较,并以满足条件的节点的结果为基础,加上自己的权值作为。
综上,
可以用数组实现,并利用此关系迭代的构造出b[n]
三、算法实现
#include <iostream> //for cin, cout, endl
using std::cin;
using std::cout;
using std::endl;
const int MAXSIZE = 1000;
int data[MAXSIZE]; //hold input
long long ans[MAXSIZE]; //ans for answer, store the result for problem, is the b[am] array
/*
* name: calculate
* description: calculate the result
* return value: long long because each input data is 32-int,
* long long that at least 64-bit is enough to hold the maximum 1000 32-int datas
* argument: n represents the number of input datas
*/
long long calculate(int n)
{
long long ret, tmp; //ret for return, represent return value, tmp for temp, used for swap data
ret = ans[0] = data[0];
for(int i=1; i<n; i++){ //calculate ans[1]~ans[n-1]
ans[i] = data[i]; //at least, ans[i] hold the data[i] value, means all data[0]~data[i-1] bigger than data[i]
for(int j=0; j<i; j++){ //find the max of ans[0]~ans[i-1]
if(data[j] < data[i]){ //if only data[j]<data[i], the ans[i] can based on ans[j]
tmp = data[i] + ans[j];
if(tmp > ans[i]) //update ans[i]
ans[i] = tmp;
}
}
if(ans[i] > ret) //update return value for each element of ans array
ret = ans[i];
}
return ret;
}
int main()
{
int N;
while( (cin>>N) && N!=0){
for(int i=0; i<N; i++)
cin >> data[i];
cout << calculate(N) << endl;
}
return 0;
}
四、总结
这个问题的分析过程是参考算法书中求最大子段和的分析过程,通过分析问题的解的形式,发现和子段和问题类似,所不同之处是求解的方式不同,这个问题的求解依赖于过去的所有元素,而在最大子段和问题中只依赖于前一个元素的结果。