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\n",f[n]);
    return 0;
} 

 

 
posted @ 2018-12-10 22:13  DukeLv  阅读(159)  评论(0编辑  收藏  举报