• P2120 [ZJOI2007]仓库建设 斜率优化dp


    好题,这题是我理解的第一道斜率优化dp,自然要写一发题解。首先我们要写出普通的表达式,然后先用前缀和优化。然后呢?我们观察发现,x【i】是递增,而我们发现的斜率也是需要是递增的,然后就维护一个单调递增就行了。

    放一个证明题解。

    设f[i]表示在i点建仓库的最小费用,易得方程:f[i]=min(f[j]+(x[i]-x[j+1])*p[j+1]+(x[i]-x[j+1])*p[j+2]...) =min(f[j]+c[i]+x[i]*(p[j+1..i])-(x[j+1]*p[j+1]+...+x[i]*p[i]))

    设s[i]=p[1]+p[2]+..p[i],ss[i]=x[1]*p[1]+...x[i]*p[i]
    f[i]=f[j]+c[i]+x[i]*(s[i]-s[j])-(ss[i]-ss[j])
    
    设j<k即s[j]<s[k],当取k更优时满足:
    f[j]+x[i]*(s[i]-s[j])+ss[j]>f[k]+x[i]*(s[i]-s[k])+ss[k]
    x[i]>(f[k]-f[j]+ss[k]-ss[j])/(s[k]-s[j])
    
    设x<y<z,cale(i,j)表示i、j间的斜率
    若cale(x,y)>cale(y,z)
     1.x[i]>cale(x,y)>cale(y,z)则z更优
     2.x[i]<cale(x,y),则x更优
    因为x[i]递增,情况1保持不变,情况2可能会变成情况1还是不可能取y
    综上当cale(x,y)>cale(y,z)时可以踢掉y,即维护斜率递增
    题干:
    题目背景
    
    小B的班级数学学到多项式乘法了,于是小B给大家出了个问题:用编程序来解决多项式乘法的问题。
    题目描述
    
    L公司有N个工厂,由高到底分布在一座山上。
    
    工厂1在山顶,工厂N在山脚。 由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用。
    
    突然有一天,L公司的总裁L先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是L先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。
    
    由于地形的不同,在不同工厂建立仓库的费用可能是不同的。第i个工厂目前已有成品Pi件,在第i个工厂位置建立仓库的费用是Ci。
    
    对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于L公司产品的对外销售处设置在山脚的工厂N,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,假设一件产品运送1个单位距离的费用是1。
    
    假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到以下数据:
    
        工厂i距离工厂1的距离Xi(其中X1=0);
        工厂i目前已有成品数量Pi;
        在工厂i建立仓库的费用Ci;
    
    请你帮助L公司寻找一个仓库建设的方案,使得总的费用(建造费用+运输费用)最小。
    输入输出格式
    输入格式:
    
    第一行包含一个整数N,表示工厂的个数。接下来N行每行包含两个整数Xi, Pi, Ci, 意义如题中所述。
    
    输出格式:
    
    仅包含一个整数,为可以找到最优方案的费用。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define duke(i,a,n) for(register int i = a;i <= n;i++)
    #define lv(i,a,n) for(register int i = a;i >= n;i--)
    #define clean(a) memset(a,0,sizeof(a))
    const int INF = 1 << 30;
    typedef long long ll;
    typedef double db;
    template <class T>
    void read(T &x)
    {
        char c;
        bool op = 0;
        while(c = getchar(), c < '0' || c > '9')
            if(c == '-') op = 1;
        x = c - '0';
        while(c = getchar(), c >= '0' && c <= '9')
            x = x * 10 + c - '0';
        if(op) x = -x;
    }
    template <class T>
    void write(T x)
    {
        if(x < 0) putchar('-'), x = -x;
        if(x >= 10) write(x / 10);
        putchar('0' + x % 10);
    }
    const int N = 1e6 + 5;
    int n,m,x[N],q[N],c[N];
    ll f[N],ss[N],s[N];
    db calc(int j,int k)
    {
        return (f[k] - f[j] + ss[k] - ss[j]) * 1.0 / (s[k] - s[j]);
    }
    int main()
    {
        read(n);
        duke(i,1,n)
        {
            read(x[i]);read(s[i]);read(c[i]);
            ss[i] = ss[i - 1] + x[i] * s[i];
            s[i] += s[i - 1];
        }
        for(int i = 1,l = 0,r = 0;i <= n;i++)
        {
            while(l < r && x[i] > calc(q[l],q[l + 1])) l++;
            f[i] = f[q[l]] + c[i] - ss[i] + ss[q[l]] + x[i] * (s[i] - s[q[l]]);
            while(l < r && calc(q[r - 1],q[r]) > calc(q[r],i)) r--;
            q[++r] = i;
        }
        printf("%lld
    ",f[n]);
        return 0;
    } 
     
  • 相关阅读:
    架构与模式11
    Unix/Linux命令
    Winform的Excel表格
    B/S结构一机多屏实现
    Effective C++函数参数传递方式
    Django & Tornado
    ThoughtWorks读书路线图
    善用泛型 委托
    SQL 关于with cube ,with rollup 和 grouping
    测试工程师实习笔试题
  • 原文地址:https://www.cnblogs.com/DukeLv/p/10099533.html
Copyright © 2020-2023  润新知