【2019牛客暑期多校训练营 第十场 J题】【Wood Processing】【DP+斜率优化】

本文探讨了一个关于合成相同高度木块的问题,通过动态规划和斜率优化技术,解决了大规模数据集下的效率问题。文章详细介绍了状态转移方程的推导过程,以及如何利用斜率优化来减少时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:
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=1maxi1(f[i][j],f[x][j1]+j=x+1i(hjhx+1)wj)
时间复杂度为 O ( k n 2 ) O(kn^2) O(kn2),对于题目数据 n ≤ 5000 , k ≤ 2000 n\leq5000,k\leq2000 n5000,k2000,显然是会超时的,考虑斜率优化
如果还没学过斜率优化,可以去洛谷上做几道斜率优化的题,如P2365 任务安排P3648 [APIO2014]序列分割等等,你就会发现这东西只要知道能够用斜率优化,并且推出式子就是完全套板子一样的题,下面是推导式子过程
这里 f [ i ] [ j − 1 ] f[i][j-1] f[i][j1]简记为 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=1ihjwj s i = ∑ j = 1 i w j s_i=\displaystyle\sum_{j=1}^iw_j si=j=1iwj
x &lt; y x&lt;y x<y,并且在 x x x处切割比 y y y处切割更优,则有:

g x + ∑ j = x + 1 i ( h j − h x + 1 ) ∗ w j &lt; 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&lt;g_y+\displaystyle\sum_{j=y+1}^i(h_j-h_{y+1})*w_j gx+j=x+1i(hjhx+1)wj<gy+j=y+1i(hjhy+1)wj

g x + S i − S x + 1 − h x + 1 ∗ ( s i − s x + 1 ) &lt; 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})&lt;g_y+S_i-S_{y+1}-h_{y+1}*(s_i-s_{y+1}) gx+SiSx+1hx+1(sisx+1)<gy+SiSy+1hy+1(sisy+1)

g x − g y + S y − S x + h x + 1 ∗ s x + 1 − h y + 1 ∗ s y + 1 &lt; 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}&lt;s_i*(h_{x+1}-h_{y+1}) gxgy+SySx+hx+1sx+1hy+1sy+1<si(hx+1hy+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 &gt; 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}}&gt;s_i hx+1hy+1gxgy+SySx+hx+1sx+1hy+1sy+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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值