zoukankan      html  css  js  c++  java
  • Problem 8 dp

    $des$

    $sol$

    记 $f_i$ 表示考虑前 $i$ 个建筑, 并且第 $i$ 个建筑的高度不变的答案, 每次
    转移时枚举上一个不变的建筑编号, 中间的一段一定变成相同的高度, 并且
    高度小于等于两端的高度.
    假设从 $f_j$ 转移且中间高度为 $t$, 则:
    $$f_i = sum_{k = j + 1} ^ {i - 1} (t - h_k) ^ 2 + c(h_j + h_i - 2t)$$
    这样中间的高度可以 $O(1)$ 求二次函数的对称轴确定. 考虑优化转移,
    因为中间高度要小于两端, 所以最多只有一个 $h_j > h_i$ 的 $j$ 能够转移. 可以
    维护关于高度的单调栈, 这样有效的转移次数就是 O(n) 的.

    $code$

    #include <bits/stdc++.h>
    
    using std::pair;
    using std::vector;
    using std::string;
    
    typedef long long ll;
    typedef pair<int, int> pii;
    
    #define fst first
    #define snd second
    #define pb(a) push_back(a)
    #define mp(a, b) std::make_pair(a, b)
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    
    template <typename T> bool chkmax(T& a, T b) { return a < b ? a = b, 1 : 0; }
    template <typename T> bool chkmin(T& a, T b) { return a > b ? a = b, 1 : 0; }
    
    template <typename T> T read(T& x) {
        int f = 1; x = 0;
        char ch = getchar();
        for(;!isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
        for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
        return x *= f;
    }
    
    const int N = 1000000;
    
    int n, C;
    int h[N + 5];
    ll s[2][N + 5], dp[N + 5];
    
    ll solve(int x, int y, int mx) {
        ll a = y - x - 1;
        ll b = -2 * (s[0][y-1] - s[0][x]) - (x != 0) * C - (y != n+1) * C;
        ll c = s[1][y-1] - s[1][x] + 1ll * (x != 0) * h[x] * C + 1ll * (y != n+1) * h[y] * C;
    
        ll t;
        t = (ll) ((- b / 2 / a) + 0.5);
    
        chkmax<ll>(t, mx);
        if(x != 0) chkmin(t, (ll) h[x]);
        if(y <= n) chkmin(t, (ll) h[y]);
    
        return a * t * t + b * t + c;
    }
    
    int main() {
    
        read(n), read(C);
        for(int i = 1; i <= n; ++i) {
            read(h[i]);
            s[0][i] = s[0][i-1] + h[i];
            s[1][i] = s[1][i-1] + 1ll * h[i] * h[i];
        }
    
        static int stk[N + 5], top;
    
        h[0] = h[n + 1] = (1 << 30);
        stk[top ++] = 0;
    
        for(int i = 1; i <= n+1; ++i) {
            dp[i] = dp[i-1] + ((i == 1 || i == n+1) ? 0 : 1ll * C * std::abs(h[i] - h[i-1]));
            while(top > 0 && h[stk[top-1]] <= h[i]) {
                if(top > 1) 
                    chkmin(dp[i], dp[stk[top-2]] + solve(stk[top-2], i, h[stk[top-1]]));
                -- top;
            }
            stk[top ++] = i;
        }
        printf("%lld
    ", dp[n + 1]);
    
        return 0;
    }
  • 相关阅读:
    201521123061 《Java程序设计》第三周学习总结
    201521123061 《Java程序设计》第二周学习总结
    201621123033 《Java程序设计》第10周学习总结
    201621123033 《Java程序设计》第9周学习总结
    201621123033 《Java程序设计》第8周学习总结
    201621123033 《Java程序设计》第7周学习总结
    201621123033 《Java程序设计》第6周学习总结
    201621123033 《Java程序设计》第5周学习总结
    201621123033 《Java程序设计》第4周学习总结
    201621123033 《Java程序设计》第3周学习总结
  • 原文地址:https://www.cnblogs.com/shandongs1/p/9783478.html
Copyright © 2011-2022 走看看