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;
}
posted @ 2019-08-19 22:18  kpole  阅读(203)  评论(0编辑  收藏  举报