2019牛客暑期多校训练营(第十场)J - Wood Processing (斜率优化DP)
题意
\(n\) 个 宽度为\(w_i\),高为\(h_i\) 的 木块,要求分成 k 组,对于每组内的所有木块,高度都变为组内最低木块的高度,宽度保持不变,求变化的最小面积。
分析
高度比较高的木块为迁就高度比较低的,所以按照高度从高到低排序
如果设\(d[i][k]\) 为前 \(i\) 个分成 k 份可以保留的最大面积,那么答案就是 \(tot-d[n][k]\) (\(tot\) 初始总面积)
考虑如何转移
\(d[i][k] = max(d[j][k-1] + (pre[i]-pre[j]) * h[i])\)
其中\(pre\)为宽度前缀和,即\(pre[i] = \sum_1^iw[i]\) 。
暴力转移复杂度较高,考虑如何优化。(这不就是个斜率优化嘛)
设\(j_1<j_2 < i\)
当满足\(d[j_1][k-1]+(pre[i]-pre[j_1])*h[i] < d[j_2][k-1] + (pre[i]-pre[j_2])*h[i]\) 时,\(j_1\) 可以从决策集中被删去,因为后者的\(j_2 要比 j_1\) 更优。
上式可以化简为\(h[i]<{d[j_2][k-1]-d[j_1][k-1]\over pre[j_2]-pre[j_1]}\)
又因为\(h[i]\) 是随着 \(i\) 单调递减的(因为前面按照高度排了序),所以当前 \(i\) 更新了答案之后,后面的 \(i+1,i+2\cdots\) 它们的\(h\) 会越来越小,所以需要维护一个斜率单调递减的决策集
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5050;
const int K = 2020;
struct node{
ll w,h;
}a[N];
bool cmp(node a,node b){
return a.h > b.h;
}
int n,k;
ll pre[N];
ll d[N][K];
int q[N],l,r;
long double slope(int x,int y,int p){
return (long double)1.0 * (d[x][p-1] - d[y][p-1]) / (pre[x] - pre[y]);
}
int main(){
scanf("%d%d",&n,&k);
ll sum = 0;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&a[i].w,&a[i].h);
sum += a[i].h * a[i].w;
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)pre[i] = pre[i-1] + a[i].w;
for(int p = 1;p <= k; p++){
l = r = 0;
for(int i=1;i <= n;i++){
while(l < r && slope(q[l],q[l+1],p) >= a[i].h){
l++;
}
int j = q[l];
d[i][p] = d[j][p-1] + a[i].h * (pre[i] - pre[j]);
while(l < r && slope(q[r],q[r-1],p) <= slope(q[r],i,p))r--;
q[++r] = i;
}
}
printf("%lld\n",sum-d[n][k]);
return 0;
}
注:转载请注明出处