[BZOJ1096][ZJOI2007][DP][斜率优化]仓库建设

[Problem Description]
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公司寻找一个仓库建设的方案,使得总的费用(建造费用+运输费用)最小。
[Algorithm]
DP 斜率优化
[Analysis]
O(n^2)的DP是比较好想的,设F[i]表示在i设立仓库,1-i的货物都能放到仓库中去,所要花费的最小的代价。
F[i] = min[F[j] + Trans(j, i)] + Cost[i], 0 <= j < i
其中Trans(j, i)表示把j之后的仓库的货物转移到i所需的代价。
很明显n ^ 2的效率是不够的。关键在于如何将Trans(j, i)转化成O(1)能求出的式子,然后进行优化。
Trans(j, i) = p[j + 1] * (x[i] - x[j + 1) + ... + p[i] * (x[i] - x[i])
化简得 Trans(j, i) = x[i] * (p[j + 1] + ... + p[i]) - p[j + 1] * x[j + 1] - ... - p[i] * x[i]
设sump[i] = sigma p[j], 1 <= j <= i
  sumxp[i] = sigma x[j] * p[j], 1 <= j <= i
则 Trans(j, i) = x[i] * (sump[i] - sump[j]) - (sumxp[i] - sumxp[j])
代回原式,得
F[i] = F[j] + x[i] * (sump[i] - sump[j]) - (sumxp[i] - sumxp[j]) + Cost[i], 其中j取能使F[i]最小的值
化简可得 F[j] + sumxp[j] = x[i]sump[j] + B
其中B = F[i] - x[i] * sump[i] + sumxp[i] - Cost[i]
这样就成了标准的斜率优化的式子,斜率优化即可 
[Pay Attention]
用LongLong
[Code]
/**************************************************************
    Problem: 1096
    User: gaotianyu1350
    Language: C++
    Result: Accepted
    Time:2380 ms
    Memory:56020 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
using namespace std;
 
const long long MAXN = 1001000;
long long x[MAXN], p[MAXN], c[MAXN];
long long sump[MAXN] = {0}, sumxp[MAXN] = {0};
long long f[MAXN] = {0};
long long n;
 
inline long long cross(long long x, long long y, long long z)
{
    return (sump[y] - sump[x]) * (f[z] + sumxp[z] - f[y] - sumxp[y]) -
           (sump[z] - sump[y]) * (f[y] + sumxp[y] - f[x] - sumxp[x]);
}
 
inline long long calc(long long i, long long j)
{
    return f[j] + x[i] * sump[i] - x[i] * sump[j] - sumxp[i] + sumxp[j] + c[i];
}
 
struct MaxQueue
{
    long long q[MAXN];
    long long head, tail;
    MaxQueue()
    {
        memset(q, 0, sizeof(q));
        head = tail = 1;
    }
    void Clear()
    {
        memset(q, 0, sizeof(q));
        head = tail = 1;
    }
    void Insert(long long x)
    {
        while (head + 1 < tail && cross(q[tail - 2], q[tail - 1], x) <= 0)
            tail--;
        q[tail++] = x;
    }
    long long Query(long long x)
    {
        while (head + 1 < tail && calc(x, q[head]) >= calc(x, q[head + 1]))
            head++;
        return q[head];
    }
}maxq;
 
int main()
{
    scanf("%lld", &n);
    for (long long i = 1; i <= n; i++)
    {
        scanf("%lld%lld%lld", &x[i], &p[i], &c[i]);
        sump[i] = sump[i - 1] + p[i];
        sumxp[i] = sumxp[i - 1] + x[i] * p[i];
    }
    maxq.Insert(0);
    for (long long i = 1; i <= n; i++)
    {
        f[i] = calc(i, maxq.Query(i));
        maxq.Insert(i);
    }
    printf("%lld\n", f[n]);
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值