「BZOJ 1010」玩具装箱-斜率优化

P 教授有编号为 $1 \sim N$ 的 $N$ 件玩具,第 $i$ 玩具经过压缩后变成一维长度为 $C_i$ 为了方便整理,P 教授要求在一个一维容器中的玩具编号是连续的。如果将第 $i$ 件玩具到第 $j$ 个玩具放到一个容器中,那么容器的长度将为 $$x = j - i + \sum\limits_{k = i} ^ j C_k$$。如果容器长度为 $x$。其制作费用为 $(x - L) ^ 2$。其 $L$ 是一个常量。P 教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 $L$。但他希望费用最小。

链接

BZOJ 1010

题解

令 $f[i]$ 表示前 $i$ 件玩具放进若干个容器中的最小费用,前缀和 $s(n) = \sum\limits_{i = 1} ^ nC[i]$。

转移时枚举前面多少个装在同一个箱子里,设它为 $j$,则第 $j + 1 \sim i$ 个装在同一个箱子里,长度为 $i - j - 1 + s(i) - s(j)$,即
$$f[i] = \min\limits_{j = 0} ^ {i - 1} \big \{ f[j] + (i - j - 1 + s(i) - s(j) - L) ^ 2 \big \}$$

直接计算的复杂度为 $O(n ^ 2)$。

考虑优化,令 $g(i) = s(i) + i - L - 1, h(j) = s(j) + j$,方程变为
$$f[i] = \min\limits_{j = 0} ^ {i - 1} \big \{ f[j] + \big [ g(i) - h(j) \big ] ^ 2 \big \}$$

考虑两个决策 $a, b(a > b)$,若 $a$ 比 $b$ 优,则
$$ \begin{aligned} f[a] + \big [ g(i) - h(a) \big ] ^ 2 & < f[b] + \big [ g(i) - h(b) \big ] ^ 2 \\ f[a] + g(i) ^ 2 + h(a) ^ 2 - 2g(i)h(a) & < f[b] + g(i) ^ 2 + h(b) ^ 2 - 2g(i)h(b) \\ f[a] + h(a) ^ 2 - 2g(i)h(a) & < f[b] + h(b) ^ 2 - 2g(i)h(b) \\ (f[a] + h(a) ^ 2) - (f[b] + h(b) ^ 2) & < 2g(i)h(a) - 2g(i)h(b) \\ (f[a] + h(a) ^ 2) - (f[b] + h(b) ^ 2) & < 2g(i) \big [h(a) - h(b) \big ] \\ \frac{(f[a] + h(a) ^ 2) - (f[b] + h(b) ^ 2)}{h(a) - h(b)} & < 2g(i) \\ \end{aligned} $$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/**
* Copyright (c) 2017, xehoth
* All rights reserved.
* 「BZOJ 1010」玩具装箱 11-09-2017
* 斜率优化
* @author xehoth
*/
#include <bits/stdc++.h>

namespace IO {

inline char read() {
static const int IN_LEN = 1000000;
static char buf[IN_LEN], *s, *t;
s == t ? t = (s = buf) + fread(buf, 1, IN_LEN, stdin) : 0;
return s == t ? -1 : *s++;
}

template <typename T>
inline void read(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return;
c == '-' ? iosig = true : 0;
}
for (x = 0; isdigit(c); c = read()) x = x * 10 + (c ^ '0');
iosig ? x = -x : 0;
}

inline void read(char &c) {
while (c = read(), isspace(c) && c != -1)
;
}

inline int read(char *buf) {
register int s = 0;
register char c;
while (c = read(), isspace(c) && c != -1)
;
if (c == -1) {
*buf = 0;
return -1;
}
do
buf[s++] = c;
while (c = read(), !isspace(c) && c != -1);
buf[s] = 0;
return s;
}

const int OUT_LEN = 1000000;

char obuf[OUT_LEN], *oh = obuf;

inline void print(char c) {
oh == obuf + OUT_LEN ? (fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf) : 0;
*oh++ = c;
}

template <typename T>
inline void print(T x) {
static int buf[30], cnt;
if (x == 0) {
print('0');
} else {
x < 0 ? (print('-'), x = -x) : 0;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 | 48;
while (cnt) print((char)buf[cnt--]);
}
}

inline void print(const char *s) {
for (; *s; s++) print(*s);
}

inline void flush() { fwrite(obuf, 1, oh - obuf, stdout); }

struct InputOutputStream {
template <typename T>
inline InputOutputStream &operator>>(T &x) {
read(x);
return *this;
}

template <typename T>
inline InputOutputStream &operator<<(const T &x) {
print(x);
return *this;
}

~InputOutputStream() { flush(); }
} io;
}

namespace {

using IO::io;
const int MAXN = 50000;
#define long long long

struct Task {
int n, l, a[MAXN];
long s[MAXN + 1], f[MAXN + 1];

template <typename T>
inline T square(const T &x) {
return x * x;
}

inline long g(const int i) { return s[i] + i - l - 1; }

inline long h(const int j) { return s[j] + j; }

inline double slope(const int a, const int b) {
return double((f[a] + square(h(a))) - (f[b] + square(h(b)))) /
double(h(a) - h(b));
}

inline void solve() {
io >> n >> l;
for (register int i = 0; i < n; i++) io >> a[i];
for (register int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i - 1];
static long q[MAXN];
register long *l = q, *r = q;
for (register int i = 1; i <= n; i++) {
while (l < r && slope(*(l + 1), *l) <= 2 * g(i)) l++;
f[i] = f[*l] + square(g(i) - h(*l));
while (l < r && slope(*r, *(r - 1)) > slope(i, *r)) r--;
*++r = i;
}
io << f[n];
}

} task;

#undef long
}

int main() {
task.solve();
return 0;
}
# DP

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×