洛谷P2120仓库建设

题目描述

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, 意义如题中所述。

输出格式:

仅包含一个整数,为可以找到最优方案的费用。

输入输出样例

输入样例#1:

3
0 5 10
5 3 100
9 6 10

输出样例#1:

32

说明

在工厂1和工厂3建立仓库,建立费用为10+10=20,运输费用为(9-5)*3 = 12,总费用32。

如果仅在工厂3建立仓库,建立费用为10,运输费用为(9-0)*5+(9-5)*3=57,总费用67,不如前者优。

对于20%的数据, N ≤500;

对于40%的数据, N ≤10000;

对于100%的数据, N ≤1000000。 所有的Xi, Pi, Ci均在32位带符号整数以内,保证中间计算结果不超过64位带符号整数。

思路:

​ 这是一道比较难的斜率优化变形题,我们可以设dp[i]代表前i个工厂的最小费用,那么:

dp[i] = dp[j] + c[i] + [j+1, i]的所有工厂的p[k] * (x[i] - x[k])的和

我们可以设A[i]代表前i个工厂的p[i]和,B[i]代表前i个工厂的p[i]*x[i]和,那么化简上面的式子,得到:

dp[i] = dp[j] + c[i] + x[i] * (A[i] - A[j]) - (B[i] - B[j])

我们发现里边有不变量,变量以及混合值,所以这是一道典型的斜率优化问题

这时我们可以设k < j且j结果优于k,那么化简得:

dp[j] - x[i]A[j] + B[j] < dp[k] - x[i]A[k] + B[k]

移项得:

((dp[j] + B[j]) - (dp[k] + B[k])) / (A[j] - A[k]) < x[i]

我们设f[i] = dp[i] + B[i],代入得:

(f[j] - f[k]) / (A[j] - A[k]) < x[i]

我们就得到了斜率,剩下的步骤所有题都雷同不再多说,直接上代码

代码:

/*************************************************************************
	> File Name: p.cpp
	> Author: Zcy
	> Mail: [email protected]
	> Created Time: 三  1/23 18:16:17 2019
 ************************************************************************/

#include <stdio.h>

#define ll long long
#define N 1000000

ll x[N + 5] = {0};
ll c[N + 5] = {0};
ll A[N + 5] = {0};
ll B[N + 5] = {0};
ll f[N + 5] = {0};
ll dp[N + 5] = {0};
int q[N + 5] = {0};
int head = 0, tail = 0;

double get(int j, int k) {
    return 1.0 * (f[j] - f[k]) / (A[j] - A[k]);
}

void set(int i, int j) {
    dp[i] = dp[j] + c[i] + x[i] * (A[i] - A[j]) - (B[i] - B[j]);
    f[i] = dp[i] + B[i];
}

int main () {
    int n, p;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld%d%lld", &x[i], &p, &c[i]);
        A[i] = A[i - 1] + p;
        B[i] = B[i - 1] + p * x[i];
    }
    q[tail++] = 0;
    q[tail++] = 1;
    set(1, 0);
    for (int i = 2; i <= n; i++) {
        while(tail - head > 1 && get(q[head + 1], q[head]) <= x[i]) {
            head++;
        }
        set(i, q[head]);
        while(tail - head > 1 && get(i, q[tail - 1]) <= get(q[tail - 1], q[tail - 2])) {
            tail--;
        }
        q[tail++] = i;
    }
    printf("%lld\n", dp[n]);
    return 0;
}

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

猜你喜欢

转载自blog.csdn.net/Ivan_zcy/article/details/88140975
今日推荐