题目链接:
https://ac.nowcoder.com/acm/contest/890/J
题意:
给你
n
n
n个宽度
w
w
w高度
h
h
h不同的木块,问你合成
k
k
k块高度相同的木块的最少浪费。
这里木块不能翻转,只能将切割高使得木块高度相同。
题解:
首先我们肯定会想到按高度从低到高排序
之后很容易想到这里可以DP
考虑
f
[
i
]
[
j
]
f[i][j]
f[i][j]为切到第
i
i
i块木块,前面的木块已经合成
j
j
j块高度相同的木块的最少花费
容易写出状态转移方程:
f
[
i
]
[
j
]
=
max
x
=
1
i
−
1
(
f
[
i
]
[
j
]
,
f
[
x
]
[
j
−
1
]
+
∑
j
=
x
+
1
i
(
h
j
−
h
x
+
1
)
∗
w
j
)
f[i][j]=\displaystyle\max_{x=1}^{i-1}(f[i][j],f[x][j-1]+\displaystyle\sum_{j=x+1}^i(h_j-h_{x+1})*w_j)
f[i][j]=x=1maxi−1(f[i][j],f[x][j−1]+j=x+1∑i(hj−hx+1)∗wj)
时间复杂度为
O
(
k
n
2
)
O(kn^2)
O(kn2),对于题目数据
n
≤
5000
,
k
≤
2000
n\leq5000,k\leq2000
n≤5000,k≤2000,显然是会超时的,考虑斜率优化
如果还没学过斜率优化,可以去洛谷上做几道斜率优化的题,如P2365 任务安排和P3648 [APIO2014]序列分割等等,你就会发现这东西只要知道能够用斜率优化,并且推出式子就是完全套板子一样的题,下面是推导式子过程
这里
f
[
i
]
[
j
−
1
]
f[i][j-1]
f[i][j−1]简记为
g
i
g_i
gi,
S
i
=
∑
j
=
1
i
h
j
∗
w
j
S_i=\displaystyle\sum_{j=1}^ih_j*w_j
Si=j=1∑ihj∗wj,
s
i
=
∑
j
=
1
i
w
j
s_i=\displaystyle\sum_{j=1}^iw_j
si=j=1∑iwj
记
x
<
y
x<y
x<y,并且在
x
x
x处切割比
y
y
y处切割更优,则有:
g x + ∑ j = x + 1 i ( h j − h x + 1 ) ∗ w j < g y + ∑ j = y + 1 i ( h j − h y + 1 ) ∗ w j g_x+\displaystyle\sum_{j=x+1}^i(h_j-h_{x+1})*w_j<g_y+\displaystyle\sum_{j=y+1}^i(h_j-h_{y+1})*w_j gx+j=x+1∑i(hj−hx+1)∗wj<gy+j=y+1∑i(hj−hy+1)∗wj
即 g x + S i − S x + 1 − h x + 1 ∗ ( s i − s x + 1 ) < g y + S i − S y + 1 − h y + 1 ∗ ( s i − s y + 1 ) g_x+S_i-S_{x+1}-h_{x+1}*(s_i-s_{x+1})<g_y+S_i-S_{y+1}-h_{y+1}*(s_i-s_{y+1}) gx+Si−Sx+1−hx+1∗(si−sx+1)<gy+Si−Sy+1−hy+1∗(si−sy+1)
即 g x − g y + S y − S x + h x + 1 ∗ s x + 1 − h y + 1 ∗ s y + 1 < s i ∗ ( h x + 1 − h y + 1 ) g_x-g_y+S_y-S_x+h_{x+1}*s_{x+1}-h_{y+1}*s_{y+1}<s_i*(h_{x+1}-h_{y+1}) gx−gy+Sy−Sx+hx+1∗sx+1−hy+1∗sy+1<si∗(hx+1−hy+1)
即 g x − g y + S y − S x + h x + 1 ∗ s x + 1 − h y + 1 ∗ s y + 1 h x + 1 − h y + 1 > s i \frac{g_x-g_y+S_y-S_x+h_{x+1}*s_{x+1}-h_{y+1}*s_{y+1}}{h_{x+1}-h_{y+1}}>s_i hx+1−hy+1gx−gy+Sy−Sx+hx+1∗sx+1−hy+1∗sy+1>si
最后我们维护一个斜率递减的单调队列即可
代码:
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define sz sizeof
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 5e3 + 10;
struct node {
ll w, h;
bool operator < (const node& a) const {
if (h == a.h)return w < a.w;
return h < a.h;
}
}a[MAX];
int n, K;
int q[MAX];
ll S[MAX], s[MAX], f[MAX], g[MAX];
double slope(int x, int y) {
if (a[x + 1].h == a[y + 1].h)return 1e18;
return 1.0 * (g[x] - g[y] + S[y + 1] - S[x + 1] + a[x + 1].h * s[x + 1] - a[y + 1].h * s[y + 1]) / (a[x + 1].h - a[y + 1].h);
}
int main() {
ios::sync_with_stdio(0);
cin >> n >> K;
for (int i = 1; i <= n; i++)
cin >> a[i].w >> a[i].h;
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++)
S[i] = S[i - 1] + a[i].h * a[i].w, s[i] = s[i - 1] + a[i].w;
memset(g, 0x3f, sz(g));
g[0] = 0;
for (int k = 1; k <= K; k++) {//已分成k份
int head = 1, tail = 0;
q[++tail] = 0;
for (int i = 1; i <= n; i++) {//当前到第i个
while (head < tail && slope(q[head], q[head + 1]) <= s[i])head++;
f[i] = g[q[head]] + S[i] - S[q[head] + 1] - a[q[head] + 1].h * (s[i] - s[q[head] + 1]);
while (head < tail && slope(q[tail], i) <= slope(q[tail - 1], q[tail]))tail--;
q[++tail] = i;
}
memcpy(g, f, sz(f));//g[i]即下一轮的f[i][j-1]
}
cout << f[n] << endl;
return 0;
}