[HNOI2008]玩具装箱(斜率优化DP)

题目

描述 Description
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1…N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过L。但他希望费用最小.
输入格式 Input Format
第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7
输出格式 Output Format
输出最小费用
样例输入 Sample Input
5 4
3
4
2
1
4
样例输出 Sample Output
1

题解

  • 首先显然的 D P DP DP
    f [ i ] = min ⁡ ( f [ j ] + ( s u m [ i ] + i − s u m [ j ] − j − L − 1 ) 2 ) f[i] = \min(f[j] + (sum[i] + i - sum[j] - j - L - 1)^2) f[i]=min(f[j]+(sum[i]+isum[j]jL1)2)
  • f [ i ] f[i] f[i]表示选到第 i i i个玩具所能得到的最小价值, s u m [ i ] sum[i] sum[i]为前缀和
  • 那么这种类型的方程式无疑需要 O ( n 2 ) O(n^2) O(n2)枚举,我们来想办法优化一下,我们设
    a [ i ] = s u m [ i ] + i , b [ i ] = s u m [ i ] + i + L + 1 a[i] = sum[i] + i,b[i] = sum[i] + i + L + 1 a[i]=sum[i]+ib[i]=sum[i]+i+L+1
  • 那么方程就变为了
    f [ i ] = min ⁡ ( f [ j ] + ( a [ i ] − b [ j ] ) 2 ) f[i] = \min(f[j] + (a[i] - b[j])^2) f[i]=min(f[j]+(a[i]b[j])2)
  • 将方程展开就变为了
    f [ i ] = min ⁡ ( f [ j ] + a [ i ] 2 − 2 ∗ a [ i ] ∗ b [ j ] + b [ j ] 2 ) f[i] = \min (f[j] + a[i] ^ 2 - 2*a[i]*b[j] + b[j]^2) f[i]=min(f[j]+a[i]22a[i]b[j]+b[j]2)
  • 1 ⩽ x , y &lt; i 1 \leqslant x, y &lt; i 1xy<i, 且 f [ x ] &lt; f [ y ] f[x] &lt; f[y] f[x]<f[y],即 x x x y y y优,那么我们便有
    f [ x ] + ( a [ i ] − b [ x ] ) 2 ⩽ f [ y ] + ( a [ i ] − b [ y ] ) 2 f[x]+(a[i]-b[x])^2 \leqslant f[y] + (a[i]-b[y])^2 f[x]+(a[i]b[x])2f[y]+(a[i]b[y])2
  • 展开得
    f [ x ] + a [ i ] 2 − 2 ∗ a [ i ] ∗ b [ x ] + b [ x ] 2 ⩽ f [ y ] + a [ i ] 2 − 2 ∗ a [ i ] ∗ b [ y ] + b [ y ] 2 f[x]+a[i]^2 - 2*a[i]*b[x]+b[x]^2 \leqslant f[y] + a[i] ^ 2 - 2 * a[i] * b[y] + b[y] ^ 2 f[x]+a[i]22a[i]b[x]+b[x]2f[y]+a[i]22a[i]b[y]+b[y]2
  • 化简得
    ( f [ x ] − f [ y ] ) + ( b [ x ] 2 − b [ y ] 2 ) b [ x ] − b [ y ] ⩽ 2 ∗ a [ i ] \frac{(f[x]-f[y]) + (b[x] ^ 2 - b[y] ^ 2)}{b[x] - b[y]} \leqslant 2*a[i] b[x]b[y](f[x]f[y])+(b[x]2b[y]2)2a[i]
  • 那么也就是说如果有 x , y x,y xy满足上面的式子便有 f [ x ] &lt; f [ y ] f[x] &lt; f[y] f[x]<f[y]
  • 如果我们把 f [ i ] + b [ i ] 2 f[i] + b[i]^2 f[i]+b[i]2当成纵坐标, b [ i ] b[i] b[i]看做横坐标
  • 那么其实就可以化为 Δ x Δ y \frac{\Delta x}{\Delta y} ΔyΔx,即一条直线的斜率 k k k
  • 当一条直线的斜率 ⩽ 2 ∗ a [ i ] \leqslant 2*a[i] 2a[i]就说明 x x x的值比 y y y
  • 因为我们的 a [ i ] a[i] a[i]是单调递增的,那么我们便可以用单调队列来进行维护
  • 具体维护过程为将决策插进单调队列中,将队头与当前点 i i i进行比较,如果当前点与对头之间的斜率比 f [ i ] f[i] f[i]小就把队首删去,对于队尾来说把每次加入 i i i后不满足单调递增的全部删去,再把 i i i插入队尾即可

code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 100;

template <typename T>
inline void read(T &s) {
    s = 0;
    T w = 1, ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
    while (isdigit(ch))  { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
    s *= w;
}

int n, l, head, tail;
int q[maxn];
double sum[maxn], f[maxn];

inline double a(int x) { return sum[x] + x; }
inline double b(int x) { return sum[x] + x + l + 1; }
inline double c(int x) { return b(x) * b(x); }
inline double slope(int x, int y) { return (f[x] + c(x) - f[y] - c(y)) / (b(x) - b(y)); }

int main() {
    read(n); read(l);
    for (int i = 1; i <= n; ++i) {
        scanf("%lf", &sum[i]);
        sum[i] += sum[i - 1];
    }
    head = tail = 1;
    for (int i = 1; i <= n; ++i) {
        while (head < tail && slope(q[head], q[head + 1]) <= 2 * a(i)) ++head;
        f[i] = f[ q[head] ] + (a(i) - b(q[head])) * (a(i) - b(q[head]));
        while (head < tail && slope(i, q[tail - 1]) < slope(q[tail - 1], q[tail])) --tail;
        q[++tail] = i;
    }
    printf("%lld\n", (long long)f[n]);
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值