zoukankan      html  css  js  c++  java
  • [bzoj1010][HNOI2008]玩具装箱TOY

    一道经典题,题目描述略。
    第一写斜率优化,好紧张啊~~~~
    首先就写了一个大暴力:
    定义f[i]为已经分配了i个玩具时的最小费用。
    方程容易写出:
    f[i] = f[j] + w[j][i];
    其中(w[j][i] = (sum_i - sum_j + i - (j+1) - l ) ^ 2)
    明显地,这是一个O(n2)的算法,只能得到题目30的分数。
    所以我们上斜率优化。
    首先我们分析复杂度:状态为O(n),转移为O(n)
    状态已经可以认为无法继续优化了(除非使用其他算法),所以我们考虑优化转移。
    曾经我们做过很多题目,可以使用单调队列来获得均摊O(1)的转移复杂度,这里也是类似思路。
    经过一些推导(这里略去,http://medalplus.com/?p=1751#Solution写的很不错),我们可以知道决策i比决策j优当且仅当(i>j)

    [frac{f[j]-f[k]+(G[j]+M)^2-(G[k]+M)^2}{2(G[j]-G[k])} < G(x) ]

    其中,G[i] = S[i] + i, 常数M = 1 + l, S[i] = S[i-1]+C[i],x是当前需要进行决策的点.
    根据G[x]的定义,可以知道G[x]是单调递增的。
    所以我们证明这样两个性质:
    性质一:
    如果我们把每一个状态抽象成一个点,那么原式可以看作i到j连线的斜率。
    我们说:对于一个决策x,其最优决策一定在x决策前面的决策的点连成的一个下凸包上。
    对于这个结论:
    考虑有一个要加入的点k>j, 且k[j-1][j] > k[j][k]
    因为j点已经在这个下凸包上,所以j一定比j-1更优,所以k[j-1][j] < G[x]
    又因为k[j][k] < k[j-1][j],
    所以k[j][k] < G[x]
    所以k比j更优
    所以我们把j点舍弃,在操作中就是从尾端弹出双端队列。
    直接把j-1点连上k即可。
    这样我们就证明了性质一。
    性质二:
    因为G[x]单调递增,所以我们可以从前向后弹出最前端元素来保证k[head][head+1] > G[i]
    这时k[head]一定是最优的。
    根据定义不难得出这个结论。
    在两个性质的推导中,我们也得到了维护下凸包的方法,具体详见代码。
    下面是代码。

    #include <bits/stdc++.h>
    #define ll long long 
    using namespace std;
    const int maxn = 50005;
    ll n, l;
    ll f[maxn];
    ll c[maxn], s[maxn], g[maxn];
    ll pow(ll n) {return n*n;}
    ll que[maxn];
    int alpha;
    int head, tail, size;
    double calck(int a, int b) {
        return (double)(f[b]-f[a]+pow(g[b]+alpha)-pow(g[a]+alpha))/(double)(2*(g[b]-g[a]));
    }
    int main() {
        scanf("%d %d", &n, &l);
        alpha = l+1;
        s[0] = 0;
        for(int i = 1; i <= n; i++) {
           scanf("%d", &c[i]); 
           s[i] = s[i-1] + c[i];
           g[i] = s[i] + i; 
        }
        f[0] = 0;
        head = 1;
        tail = 1;
        size = 1;
        que[head] = 0;
        for(int i = 1; i <= n; i++) {
            while(size >= 2) {
                int a = que[head];
                int b = que[head+1];
                if(calck(a, b) < g[i]) {
                    head++;
                    size--;
                }
                else break;
            }   
            int n = que[head];
            f[i] = f[n]+pow(s[i]-s[n]+i-n-1-l);
            if(size>=2){int x = que[tail];
            int y = que[tail-1];
            while(calck(x, i) < calck(y, x)) {
                tail--;
                size--;
                if(size < 2) break;
                x = que[tail];
                y = que[tail-1];
            }}
            tail++;
            que[tail] = i;
            size++;
        } 
        //for(int i = 1; i <= n; i++) cout << f[i] << ' ';
        //cout << endl;
        printf("%lld
    ", f[n]);
    }
    
  • 相关阅读:
    进度条
    html5 表单新增事件
    html5 表单的新增type属性
    html5 表单的新增元素
    html5 语义化标签
    jq 手风琴案例
    codeforces 702D D. Road to Post Office(数学)
    codeforces 702C C. Cellular Network(水题)
    codeforces 702B B. Powers of Two(水题)
    codeforces 702A A. Maximum Increase(水题)
  • 原文地址:https://www.cnblogs.com/gengchen/p/6340921.html
Copyright © 2011-2022 走看看