• [HNOI2008]玩具装箱TOY


    Portal

    这一题是斜率优化的入门题,应该写一些文字来总结.

    斜率优化是一种针对dp方程式中有一个或数个单项式同时含有IJ相关的内容的一种优化。


    首先列出Dp方程式:$$ Dp[i] = min{Dp[j] + (sum[i] - sum[j] + i - j - l - 1)^2}$$

    I,J,为主元进行分类。我们先忽略Min

    严格意义来说,这里的常数项为了保持式子的优美应该单独考虑,这里因为要尽量简化Dp[i]的求解, 我们将其

    [Dp[i] = Dp[j] + ((sum[i] + i) - (sum[j] + j + l + 1)) ^ 2\ Dp[i] = Dp[j] + (sum[i] + i)^2 + (sum[j] + j + l + 1) ^ 2 - 2 * (sum[i] + i) * (sum[j] + j + l + 1)\ 令sum[i] + i = A_i, sum[i] + i + l + 1 = B_i\ Dp[i] = Dp[j] + A_i^2 + B_j^2 - 2 * A_i * B_j\ 再次分类:\ dp[j] + B_j^2 = 2 * A_i * B_i + dp[i] - A_i^2 ]

    这个式子里,我们初中的点斜式的斜率k就是(2 * A_i),截距就是(dp[i] - A_i^2) 这里我们知道了斜率,要最小化截距, 并且这里面的点是((x = B_i, y = dp[j] + B_j^2))

    发现如果让截距变小的话就维护这些点的一个下凸壳。一个斜率固定的直线一定会跟这个凸壳的斜率最相近的点先碰到。

    观察到斜率是单调递增的, 于是我们可以用一个单调队列维护。

    最后一定要维护凸壳的凸性质。所以要判断队列的最后三个点组成的三角形是否方向向左。

    总结

    如果是取最大值,那就维护上凸壳,取最小值,那就维护下凸壳。
    如果k((2 * A_i))没有单调性-> 凸壳上二分。

    如果x((B_i))-> 数据结构维护。

    Codes

    #include<bits/stdc++.h>
    using std :: deque;
    #define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
    #define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
    #define clar(a, b) memset((a), (b), sizeof(a))
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    typedef long long LL;
    typedef long double LD;
    int read() {
        char ch = getchar();
        int x = 0, flag = 1;
        for (;!isdigit(ch); ch = getchar()) if (ch == '-') flag *= -1;
        for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
        return x * flag;
    }
    void write(int x) {
        if (x < 0) putchar('-'), x = -x;
        if (x >= 10) write(x / 10);
        putchar(x % 10 + 48);
    }
    
    #define sqr(x) ((x) * (x))
    const int Maxn = 50009;
    static int n, l, a[Maxn];
    static LL sum[Maxn];
    
    void init() {
    	n = read(); l = read();
    	rep (i, 1, n) {
    		a[i] = read();
    		sum[i] = sum[i - 1] + a[i];
    	}
    }
    
    LL dp[Maxn], que[Maxn], fr, ls;
    double slope(int a, int b) {
    	return (dp[a] + sqr(sum[a] + a + l + 1ll) - dp[b] - sqr(sum[b] + b + l + 1ll)) / (sum[a] + a - sum[b] - b);
    }
    
    void solve() {
    	rep (i, 1, n) {
    		while (fr < ls && slope(que[fr], que[fr + 1]) < 2 * (sum[i] + i)) ++fr;
    		dp[i] = dp[que[fr]] + sqr(sum[i] + i - sum[que[fr]] - que[fr] - l - 1);
    		while (fr < ls && slope(que[ls], que[ls - 1]) > slope(i, que[ls])) --ls;
    		que[++ls] = i;
    	}
    
    	printf("%lld
    ", dp[n]);
    }
    
    int main() {
    	freopen("BZOJ1010.in", "r", stdin);
    	freopen("BZOJ1010.out", "w", stdout);
    
    	init();
    	solve();
    
    #ifdef Qrsikno
        debug("
    Running time: %.3lf(s)
    ", clock() * 1.0 / CLOCKS_PER_SEC);
    #endif
        return 0;
    }
    
  • 相关阅读:
    Docker之Harbor
    idea 代码块编辑(批量列编辑)快捷键 -- idea version 2018 不常用
    mysql 去除表中重复的数据,保留id最小的数据信息
    打家劫舍(动态规划+滚动数组+取模运算优化)
    利用线程异步调用
    idea 2019激活码
    mysql导出PDM表结构并带有注释
    安装GO
    GO语言
    项目启动
  • 原文地址:https://www.cnblogs.com/qrsikno/p/10140272.html
Copyright © 2020-2023  润新知