[HNOI2008]玩具装箱TOY——[斜率优化DP]

【题目描述】

P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为 1 ⋯ N 1\cdots N 1N N N N 件玩具,第 i i i 件玩具经过压缩后变成一维长度为 C i C_i Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第 i i i 件玩具到第 j j j 个玩具放到一个容器中,那么容器的长度将为 x = j − i + ∑ k = i j C k x=j-i+\sum\limits_{k=i}^{j}C_k x=ji+k=ijCk制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 x x x ,其制作费用为 ( X − L ) 2 (X-L)^2 (XL)2.其中 L L L 是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 L L L 。但他希望费用最小.

【输入格式】

第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7

【输出格式】

输出最小费用

S a m p l e    I n p u t Sample~~Input Sample  Input

5 4
3
4
2
1
4

S a m p l e    O u t p u t Sample~~Output Sample  Output

1

【题意分析】

做题前确保你已经读懂了题意。
s[i]a[i]前缀和,f[i]=s[i]+ig[i]=s[i]+i-L-1
暴力DP:

d p i = min ⁡ 1 ≤ j < i { d p j + ( s i + i − s j − j − L − 1 ) 2 } dp_i=\min _{1\leq j< i} \{ dp_j +(s_i+i-s_j-j-L-1)^2\} dpi=1j<imin{dpj+(si+isjjL1)2}

⇒ d p i = min ⁡ 1 ≤ j < i { d p j + ( g i − f j ) 2 } \Rightarrow dp_i=\min _{1\leq j< i} \{ dp_j+(g_i-f_j)^2\} dpi=1j<imin{dpj+(gifj)2}

瞬间炸裂,考虑斜率优化,令 x , y x,y x,y为两个决策且x比y大。
那么得到这个柿子:

d p x + ( g i − f x ) 2 < d p y + ( g i − f y ) 2 dp_x+(g_i-f_x)^2<dp_y+(g_i-f_y)^2 dpx+(gifx)2<dpy+(gify)2

⇒ ( d p x + f x 2 ) − ( d p y + f y 2 ) f x − f y < 2 g i \Rightarrow \frac{(dp_x+f_x^2)-(dp_y+f_y^2)}{f_x-f_y}<2g_i fxfy(dpx+fx2)(dpy+fy2)<2gi

快乐斜率优化即可。

Code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#define MAXN 1000010
#define db double
#define int long long
using namespace std;

int sum[MAXN], q[MAXN], dp[MAXN], n, L;

inline int read () {
	register int s = 0, w = 1;
	register char ch = getchar ();
	while (! isdigit (ch)) {if (ch == '-') w = -1; ch = getchar ();}
	while (isdigit (ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar ();}
	return s * w;
}

inline int f (int x) {return x + sum[x];}
inline int g (int x) {return x + sum[x] - 1 - L;}
inline int sqr (int x) {return x * x;}
inline db slope (int x, int y) {return 1.0 * ((dp[x] + sqr (f (x))) - (dp[y] + sqr (f (y)))) / (f (x) - f (y));}

signed main () {
	n = read (), L = read ();
	for (register int i = 1; i <= n; i++) sum[i] = sum[i - 1] + read ();
	int h = 0, t = 0;
	for (register int i = 1; i <= n; i++) {
		while (h < t && slope (q[h + 1], q[h]) < g (i) * 2) h++;
		dp[i] = dp[q[h]] + sqr (sum[i] + i - sum[q[h]] - q[h] - L - 1);
		while (h < t && slope (q[t], q[t - 1]) > slope (i, q[t])) t--;
		q[++t] = i;
	}
	return printf ("%lld\n", dp[n]), 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值