动态规划专题-专项训练:斜率优化 DP

1. 前言

本篇博文是斜率优化 DP 的习题博文。

讲解斜率优化 DP 的博文的传送门:动态规划专题-学习笔记:斜率优化 DP

说句实在话,斜率优化的题目只要推出转移方程, y = k x + b y=kx+b y=kx+b 还不好推吗?

注意本文全是单调队列斜率优化,如果想看一般的李超线段树斜率优化的看这篇:数据结构专题-学习笔记:李超线段树

2. 练习题

题单:

P4360 [CEOI2004]锯木厂选址

注意以下叙述对所有数据做了一个翻转处理。

f i f_i fi 表示当前从 1 到 i i i i i i 作为一个锯木厂点的最小花费。

考虑枚举另外一个锯木厂点 j < i j<i j<i,那么有转移方程:

f i = min ⁡ { t o t − d j × ( w i − 1 − w j − 1 ) − d i × ( w n − w i − 1 ) } f_i=\min\{tot-d_j\times (w_{i-1}-w_{j-1})-d_i\times (w_n-w_{i-1})\} fi=min{totdj×(wi1wj1)di×(wnwi1)}

其中的 d , w d,w d,w 做了前缀和处理, t o t tot tot 表示将所有木头移到第 0 个点的花费。

然后就是斜率优化拆拆拆,码码码。

代码:

/*
========= Plozia =========
    Author:Plozia
    Problem:P4360 [CEOI2004]锯木厂选址
    Date:2021/4/14
========= Plozia =========
*/

#include <bits/stdc++.h>

typedef long long LL;
const int MAXN = 20000 + 10;
int n, w[MAXN], d[MAXN], tot, sumw[MAXN], sumd[MAXN], ans = 0x7f7f7f7f, f[MAXN];
int q[MAXN], l, r;

int read()
{
    int sum = 0, fh = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
    return sum * fh;
}
int Min(int fir, int sec) { return (fir < sec) ? fir : sec; }
int Max(int fir, int sec) { return (fir > sec) ? fir : sec; }

int X(int x) { return sumd[x]; }
int Y(int x) { return sumd[x] * sumw[x - 1]; }
int K(int x) { return sumw[x - 1]; }
double Slope(int x, int y) { return ((double)Y(x) - Y(y)) / ((double)X(x) - X(y)); }

int main()
{
    memset(f, 0x7f, sizeof(f));
    n = read();
    for (int i = n; i >= 1; --i) w[i] = read(), d[i] = read();
    for (int i = 1; i <= n; ++i) sumw[i] = sumw[i - 1] + w[i], sumd[i] = sumd[i - 1] + d[i];
    for (int i = 1; i <= n; ++i) tot += w[i] * sumd[i];
    q[l = r = 1] = 1;
    for (int i = 2; i <= n; ++i)
    {
        while (l < r && Slope(q[l], q[l + 1]) <= (double)K(i)) ++l;
        int j = q[l]; f[i] = tot - sumd[j] * (sumw[i - 1] - sumw[j - 1]) - sumd[i] * (sumw[n] - sumw[i - 1]);
        while (l < r && Slope(i, q[r]) <= Slope(q[r], q[r - 1])) --r;
        q[++r] = i;
    }
    // for (int i = 2; i <= n; ++i)
    //     for (int j = 1; j < i; ++j)
    //         f[i] = Min(f[i], tot - sumd[j] * (sumw[i - 1] - sumw[j - 1]) - sumd[i] * (sumw[n] - sumw[i - 1]));
    for (int i = 2; i <= n; ++i) ans = Min(ans, f[i]);
    // for (int i = 2; i <= n; ++i) std::cout << f[i] << "\n";
    printf("%d\n", ans); return 0;
}

P3195 [HNOI2008]玩具装箱

f i f_i fi 表示处理了 [ 1 , i ] [1,i] [1,i] 的所有玩具时的最小总费用。

那么有状态转移方程: f i = min ⁡ { f j + ( i − j − 1 + c i − c j − l ) 2 ∣ j < i } f_i=\min\{f_j+(i-j-1+c_i-c_j-l)^2|j<i\} fi=min{fj+(ij1+cicjl)2j<i}

同样的, c c c 做了前缀和处理。

然后还是斜率优化拆拆拆,码码码。

代码:

/*
========= Plozia =========
    Author:Plozia
    Problem:P3195 [HNOI2008]玩具装箱
    Date:2021/4/15
========= Plozia =========
*/

#include <bits/stdc++.h>

typedef long long LL;
const int MAXN = 5e4 + 10;
int n, L, l, r, q[MAXN];
LL f[MAXN], c[MAXN];

int read()
{
    int sum = 0, fh = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
    return sum * fh;
}
LL Min(LL fir, LL sec) { return (fir < sec) ? fir : sec; }
LL Max(LL fir, LL sec) { return (fir > sec) ? fir : sec; }

double K(int i) { return 2.0 * (i + c[i]); }
double X(int i) { return 1.0 * i + c[i]; }
double Y(int i) { return 1.0 * f[i] + (i + c[i]) * (i + c[i]) + 2 * (i + c[i]) * (L + 1); }
double Slope(int x, int y) { return (Y(x) - Y(y)) / (X(x) - X(y)); }

int main()
{
    n = read(), L = read();
    for (int i = 1; i <= n; ++i) c[i] = read() + c[i - 1];
    q[l = r = 1] = 0;
    for (int i = 1; i <= n; ++i)
    {
        while (l < r && Slope(q[l], q[l + 1]) <= K(i)) ++l;
        int j = q[l]; f[i] = f[j] + (i - j - 1 + c[i] - c[j] - L) * (i - j - 1 + c[i] - c[j] - L);
        while (l < r && Slope(q[r], i) <= Slope(q[r], q[r - 1])) --r;
        q[++r] = i;
    }
    // for (int i = 1; i <= n; ++i)
        // for (int j = 0; j < i; ++j)
            // f[i] = Min(f[i], f[j] + (i - j - 1 + c[i] - c[j] - l) * (i - j - 1 + c[i] - c[j] - l));
    printf("%lld\n", f[n]); return 0;
}

3. 总结

斜率优化的题目就是推暴力的方程,没了~

当然有一部分题目还是很难的,仅斜率优化可能不够,就需要一些科技来进一步优化。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值