HDU -- Super Jumping! Jumping! Jumping!(ACM Step: 3.2.3)

一、概述

1. 问题描述

有n个节点,下标1~n,每个节点有权值,记做v[i],现需要找到一个递增下标序列 \begin{Bmatrix} a_{1}, & a_{2}, & ..., & a_{m} \end{Bmatrix}

其中 a_{i} < a_{i+1} ,v[a_{i}]< v[a_{i+1}],使得该序列对应的权值之和最大。

2. 问题链接

HDU -- Super Jumping! Jumping! Jumping!(ACM Step: 3.2.3)

3. 问题截图

 

图1.1 问题截图

二、算法思路

首先分析问题,问题所求可以用下述公式表示:

\underset{1 \leqslant a_{i} < a_{i+1} \leqslant a_{m}, 1 \leqslant a_{m} \leqslant n, v[a_{i}] < v[a_{i+1}], i \in \left \{ 1,2,...,m-1 \right \}}{max}\sum_{k=1}^{m}v[a_{k}]

公式等价于下式(将a_{m}的范围提出括号):

\underset{1 \leqslant a_{m} \leqslant n}{max}\left ( \underset{1 \leqslant a_{i}<a_{i+1} \leqslant a_{m}, v[a_{i}]<v[a_{i+1}],i\in \left \{ 1,2,...,m-1 \right \}}{max} \sum_{k=1}^{m} v[a_{k}] \right )

单独看括号内的公式时,它表示:a_{m}是解序列的最后一个节点时满足条件的节点(同时满足节点序号递增、以及节点权值递增条件的节点)的最大权值和。将此公式记为b[a_{m}],即有下式:

\underset{1 \leqslant \, a_{m} \, \leqslant n}{max} \: b[a_{m}]

那么b[a_{m}]之间有什么关系呢?

a_{m}=1时,b[1],表示当节点1,作为解序列的最后一个节点,此时满足条件的只有a_{m}一个节点,即此时,b[1]=v[1]

a_{m}=2时,b[2],和b[1]有没有关系呢?可以想象,b[1],是节点1作为解序列的最后一个节点的最大权值和,如果此时有条件v[1]< v[2]成立,那么节点2可以用节点1时的最大权值和为基础,加上自己的权值作为最大权值和,即b[2]=b[1]+v[2];若v[1] \geqslant v[2],此时节点2不能用到节点1的结果,此时b[2]=v[2]

a_{m}=3时,同样的,此时,节点1和节点2作为解序列的最后节点的最大权值和都已经算出,节点3可以分别取和两个节点的权值比较,并以满足条件的节点的结果为基础,加上自己的权值作为b[3]

综上,

b[1]=v[1], b[i],i \in \left \{ 2,...,n \right \}= \left ( \underset{v[j]<v[i],j \in { 1,2,...,i-1}}{max}v[j] \right ) +v[i]

可以用数组实现b[a_{m}],并利用此关系迭代的构造出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;
}

四、总结

这个问题的分析过程是参考算法书中求最大子段和的分析过程,通过分析问题的解的形式,发现和子段和问题类似,所不同之处是b[a_{m}]求解的方式不同,这个问题的求解依赖于过去的所有元素,而在最大子段和问题中只依赖于前一个元素的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值