玩具装箱

Description

W H Y WHY WHY 喜欢玩具,家里有 N N N 个玩具,有一天,她想让玩具们晒晒太阳,WHY把所有的玩具都拿出来摆成一排,从左到右依次编号为 1 ~ N 1~N 1N 。每个玩具大小不一,玩具 i i i 的大小为 A i A_i Ai

太阳下山了, W H Y WHY WHY 开始收玩具了,现在将这些玩具从左开始依次装成若干箱,每个箱子只能装最多 M M M 个且编号连续的玩具。在某个箱子里装若干个玩具的费用计算方法:如果该箱子里最大的玩具为 a a a ,最小的为 b b b ,玩具个数为 s s s ,则费用为 K + s ∗ ( a − b ) K+s*(a-b) K+s(ab) 。这里的 K K K 是箱子本身的费用,所有箱子的费用都相等。

W H Y WHY WHY 想知道把将所有玩具都装进箱里的最小费是多少?

Input

第一行为 3 3 3 个整数 N , M , K N, M, K N,M,K ,用空格分隔。 N N N 为玩具个数, M M M 为每个箱子最多能装的玩具个数, K K K 为箱子本身的费用。

以下 N N N 行第 i i i ( 1 < = i < = N ) (1<=i<=N) (1<=i<=N) 为整数 A i A_i Ai,表示第 i i i 个玩具的大小。

Output

输出为一个整数,表示装箱费用总和的最小值。

Sample Input

6 3 6
1
2
3
1
2
1

Sample Output

21

HINT

样例解释:

第一个箱子里装玩具 1 ~ 3 1~3 13,第二个里装 4 ~ 6 4~6 46,开销总和为 ( 6 + 3 × ( 3 − 1 ) ) + ( 6 + 3 × ( 2 − 1 ) ) = 21 (6+3×(3−1))+(6+3×(2−1)) = 21 (6+3×(31))+(6+3×(21))=21 。这是最小可能的开销总和,所以输出 21 21 21

【数据范围】

60   % 60 \ \% 60 % 的数据满足: N , M < = 20 N,M<=20 N,M<=20

80   % 80 \ \% 80 % 的数据满足: N < = 2000 , M < = 100 N<=2 000,M<=100 N<=2000M<=100

100   % 100 \ \% 100 % 的数据满足: 1 < = N < = 20000 , 1 < = M < = 1000 , 0 < = K < = 1000000000 , 1 < = A i < = 1000000000 ( 1 < = i < = N ) , M < = N 1<=N<=20 000,1<=M<=1 000,0<=K<=1 000 000 000,1<=Ai<=1 000 000 000 (1<=i<=N),M<=N 1<=N<=200001<=M<=10000<=K<=10000000001<=Ai<=1000000000(1<=i<=N)M<=N

题目大意

给你一串有 n n n 个数的数列 a a a ,将他分成若干段,每一段的数的个数不超过 m m m ,每一段的价值为 k + l e n ∗ ( m a x − m i n ) k+len*(max-min) k+len(maxmin),其中 k k k 为定值, m a x max max 为这一段中的最大值, m i n min min 为这一段的最小值, l e n len len 为这一段的长度,求这个数列的总价值。

解题思路

先思考数列具有的性质:

  • 无后效性:即在处理一个数 A i A_i Ai 时,他后面的数对他不产生任何影响。
  • 最优子结构:当问题的最优解包含子问题的最优解时,称该问题具有最优子结构性质。
  • 子问题重叠:在用递归算法自顶向下解此问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。

根据这些原理,想都不用想 ,正解肯定是动态规划!!!

f i f_i fi 为以 A i A_i Ai 为结尾的子段的最小价值

结尾有了,那开头呢?

于是可以,遍历 f i ~ f i − m + 1 f_i ~ f_{i-m+1} fifim+1,找到最小的 f [ j ] f[j] f[j] ,即为开头,然后将他们连起来,是不是很像最长下降子序列的思路呢?

注意,在循环时,直接 O ( 1 ) O(1) O(1) 求区间最大值 m x mx mx 和最小值 m n mn mn

由此,转移方程为:

∑ i = 1 n ∑ j = i m a x ( 1 , i − m + 1 ) f i = f j + ( ( i − j + 1 ) ∗ ( m x − m n ) + k ) \sum\limits_{i=1}^{n}\sum\limits_{j=i}^{max(1,i-m+1)}f_i=f_j+((i-j+1)*(mx-mn)+k) i=1nj=imax(1,im+1)fi=fj+((ij+1)(mxmn)+k)

A C   c o d e AC \ code AC code

#include <bits/stdc++.h>
//#define max(a,b) a>b ? a:b
//#define min(a,b) a<b ? a:b
#define int long long
using namespace std;
const int maxn = 20005;
int n, m, k;
int a[maxn], f[maxn];
signed main()
{
    scanf("%lld%lld%lld",&n,&m,&k);
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld",&a[i]);
        f[i] = 1e18;
        int mx = -1e18, mn = 1e18;
        for (int j = i; j >= max(1ll, i - m + 1); j--)
        {
            mx = max(mx, a[j]);
            mn = min(mn, a[j]);
            f[i] = min(f[i], f[j - 1] + (i - j + 1) * (mx - mn) + k);
        }
    }
    cout << f[n] << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值