洛谷算法题解:Bear and Bowling

题目描述

题面翻译

  • 给定一个长度为 n n n 的序列 a 1 … n a_{1\dots n} a1n
  • 你要求一个 a a a 的子序列 b 1 … m b_{1\dots m} b1m(可以为空),使得 ∑ i = 1 m i b i \sum_{i=1}^m ib_i i=1mibi 的值最大。
  • n ≤ 1 0 5 n \le 10^5 n105 ∣ a i ∣ ≤ 1 0 7 |a_i| \le 10^7 ai107
  • 时间限制:6秒。

题目描述

Limak is an old brown bear. He often goes bowling with his friends. Today he feels really good and tries to beat his own record!

For rolling a ball one gets a score — an integer (maybe negative) number of points. Score for i i i -th roll is multiplied by i i i and scores are summed up. So, for k k k rolls with scores s 1 , s 2 , . . . , s k s_{1},s_{2},...,s_{k} s1,s2,...,sk , total score is . Total score is 0 0 0 if there were no rolls.

Limak made n n n rolls and got score a i a_{i} ai for i i i -th of them. He wants to maximize his total score and he came up with an interesting idea. He will cancel some rolls, saying that something distracted him or there was a strong wind.

Limak is able to cancel any number of rolls, maybe even all or none of them. Total score is calculated as if there were only non-canceled rolls. Look at the sample tests for clarification. What maximum total score can Limak get?

输入格式

The first line contains single integer n n n ( 1 < = n < = 1 0 5 1<=n<=10^{5} 1<=n<=105 ).

The second line contains n n n space-separated integers a 1 , a 2 , . . . , a n a_{1},a_{2},...,a_{n} a1,a2,...,an ( ∣ a i ∣ < = 1 0 7 ) |a_{i}|<=10^{7}) ai<=107) - scores for Limak’s rolls.

输出格式

Print the maximum possible total score after choosing rolls to cancel.

样例 #1

样例输入 #1

5
-2 -8 0 5 -3

样例输出 #1

13

样例 #2

样例输入 #2

6
-10 20 -30 40 -50 60

样例输出 #2

400

提示

In first sample Limak should cancel rolls with scores − 8 -8 8 and − 3 -3 3 . Then he is left with three rolls with scores − 2 , 0 , 5 -2,0,5 2,0,5 . Total score is 1 ⋅ ( − 2 ) + 2 ⋅ 0 + 3 ⋅ 5 = 13 1·(-2)+2·0+3·5=13 1(2)+20+35=13 .

In second sample Limak should cancel roll with score − 50 -50 50 . Total score is 1 ⋅ ( − 10 ) + 2 ⋅ 20 + 3 ⋅ ( − 30 ) + 4 ⋅ 40 + 5 ⋅ 60 = 400 1·(-10)+2·20+3·(-30)+4·40+5·60=400 1(10)+220+3(30)+440+560=400 .

算法思想(动态规划)

状态表示

f [ i ] [ j ] f[i][j] f[i][j]表示在数列的前 i i i个数中已经选择 j j j个数时的最大值。

状态计算

f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − 1 ] + j ∗ x i , f [ i − 1 ] [ j − 2 ] + ( j − 1 ) ∗ x i , . . . , f [ i − 1 ] [ 1 ] + 2 ∗ x i , f [ i − 1 ] [ 0 ] + x i } f[i][j]=max\{f[i-1][j], f[i-1][j-1]+j*x_i,f[i-1][j-2]+(j-1)*x_i,...,f[i-1][1]+2*x_i,f[i-1][0]+x_i\} f[i][j]=max{f[i1][j],f[i1][j1]+jxi,f[i1][j2]+(j1)xi,...,f[i1][1]+2xi,f[i1][0]+xi},其中 j < = i j<=i j<=i

由于第 i i i阶段的状态只与 i − 1 i-1 i1阶段的状态有关,因此可以进行状态优化,使用一维状态进行计算,即 f [ j ] = m a x { f [ j ] , f [ j − 1 ] + j ∗ x i , f [ j − 2 ] + ( j − 1 ) ∗ x i , . . . , f [ 1 ] + 2 ∗ x i , f [ 0 ] + x i } f[j] = max\{f[j], f[j-1]+j*x_i,f[j-2]+(j-1)*x_i,...,f[1]+2*x_i,f[0]+x_i\} f[j]=max{f[j],f[j1]+jxi,f[j2]+(j1)xi,...,f[1]+2xi,f[0]+xi}

需要注意的是,在计算第 i i i阶段的状态时,只能使用 i − 1 i-1 i1阶段的状态,为了防止状态污染,需要从大到小枚举 j j j

如果从小到大枚举 j j j,那么在计算 f [ j ] f[j] f[j]之前, f [ j − 1 ] f[j-1] f[j1]将会被更新为第 i i i阶段的状态,与优化之前不符,称为状态污染

初始状态

由于数列中存在负数,因此求最大值时,f[j]应输出化为 − ∞ -∞ ;除此之外, f [ 0 ] = 0 f[0]=0 f[0]=0

代码实现

#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 100010;
LL f[N];
int main()
{
    LL n, x;
    scanf("%lld", &n);
    memset(f, 0xbf, sizeof f); //将f初始化为无穷小
    f[0] = 0;
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%lld", &x);
        for(int j = i; j > 0; j --)
            f[j] = max(f[j], f[j - 1] + j * x);
    }
    LL ans = -1e18;
    for(int i = 1; i <= n; i ++) ans = max(ans, f[i]);
    printf("%lld", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少儿编程乔老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值