zoukankan      html  css  js  c++  java
  • bzoj 4518: [Sdoi2016]征途 斜率优化dp

    题目大意:

    给你一个长为n的序列,要求将这个序列分成m段,使得每段内数字之和构成的方差最小.输出这个最小方差与(m^2)的乘积

    题解:

    如果我们设(s_i)表示值,(sum)表示所有值的和,那么我们有

    [ans = frac{sum_{i=1}^m(s_i - frac{sum}{m})^2}{m}*m^2 ]

    化简得(ans = m*sum_{i=1}^ms_i^2 - sum^2)
    所以我们dp一下求前面的式子:
    (f[i][j])表示将前i个数划分成了j段的各段平方和最小值.
    我们令(S_i = sum_{k=1}^is_i)即前缀和
    于是可以列出dp方程:(f[i][j] = min{f[k][j-1] + (S_i - S_k)^2})
    所以我们可以分层dp,然后用斜率优化来搞一搞
    这样就(O(nm))了 ~~

    Code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(ll &x){
        x=0;char ch;bool flag = false;
        while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
        while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    const ll maxn = 3010;
    ll f[maxn],s[maxn],tmp[maxn];
    ll q[maxn],l,r;
    #define g(x) (tmp[x] + s[x]*s[x])
    inline double T(ll p,ll q){
        return (double)(g(p) - g(q))/(double)(s[p] - s[q]);
    }
    ll a[maxn];
    int main(){
        ll n,k;read(n);read(k);
        ll sum = 0;
        for(ll i=1;i<=n;++i){
            read(a[i]);sum += a[i];
            s[i] = s[i-1] + a[i];
            tmp[i] = s[i]*s[i];
        }
        for(ll j=2;j<=k;++j){
            l = 0;r = -1;q[++r] = j-1;
            for(ll i=j;i<=n;++i){
                while(l < r && T(q[l],q[l+1]) < 2.0*s[i]) ++ l;
                f[i] = tmp[q[l]] + (s[i] - s[q[l]])*(s[i] - s[q[l]]);
                while(l < r && T(q[r-1],q[r]) > T(q[r],i)) -- r;
                q[++r] = i;
            }memcpy(tmp,f,sizeof f);
        }printf("%lld
    ",f[n]*k - sum*sum); getchar();getchar();
        return 0;
    }
    
  • 相关阅读:
    一道某高大上互联网公司的笔试题分享
    人机博弈-吃子棋游戏(四)搜索算法
    人机博弈-吃子棋游戏(三)走法生成
    人机博弈,吃子棋游戏(二)如何算气
    eclipse手动导入dtd文件
    spring BeanFactory概述
    xp的虚拟机如何访问本地主机上的文件
    XML Schema 简介
    DTD 简介
    spring开发相关网址
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6522909.html
Copyright © 2011-2022 走看看