P2120 [ZJOI2007] 仓库建设题解

题目描述

L 公司有 n 个工厂,由高到低分布在一座山上,工厂 1 在山顶,工厂 n 在山脚。

由于这座山处于高原内陆地区(干燥少雨),L 公司一般把产品直接堆放在露天,以节省费用。突然有一天,L 公司的总裁 L 先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是 L 先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。

由于地形的不同,在不同工厂建立仓库的费用可能是不同的。第 i 个工厂目前已有成品 pi​ 件,在第 i 个工厂位置建立仓库的费用是 ci​。

对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于 L 公司产品的对外销售处设置在山脚的工厂 n,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,一件产品运送一个单位距离的费用是 1。

假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到以下数据:

  • 工厂 i 距离工厂 1 的距离 xi​(其中 x1​=0)。
  • 工厂 i 目前已有成品数量 pi​。
  • 在工厂 i 建立仓库的费用 ci​。

请你帮助 L 公司寻找一个仓库建设的方案,使得总的费用(建造费用 + 运输费用)最小。

输入格式

输入的第一行是一个整数 n,代表工厂的个数。

第 2 到 (n+1) 行,每行有三个用空格隔开的整数,第 (i+1) 行的整数依次代表 xi​, pi​, ci​。

输出格式

仅输出一行一个整数,代表最优方案的费用。

输入输出样例

输入 #1复制

3
0 5 10
5 3 100
9 6 10

输出 #1复制

32

说明/提示

样例输入输出 1 解释

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

数据范围与约定

对于 20% 的数据,保证 n≤500。

对于 40% 的数据,保证 n≤104。

对于 100% 的数据,保证 1≤n≤106,0≤xi​,pi​,ci​<231。

对于任意的 1≤i<n,保证 xi​<xi+1​。

设答案为 ans,保证 ans+i=1∑n​pi​xi​<263。

思路

1024程序员节第一篇题解。

首先,可以发现,设fi为选第i个建仓库且前i个均安排好的数量,则答案为fq,q为其后所有产品数都为0的最小仓库编号。

可得:

f_i=\min_{j=0}^{i-1}(f_j+c_i-c_j-b_j*ax_i+b_j*ax_j+ac_i)

其中

b_i=b_{i-1}+ap_i

c_i=c_{i-1}+b_{i-1}*(ax_i-ax_{i-1})

代码:

#include<bits/stdc++.h>
using namespace std;
long long n,f[1000006],b[1000006],c[1000006],as=1e18+7,op=0;
struct one{
    long long x,p,c;
}a[1000006];
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i].x>>a[i].p>>a[i].c;
    }
    for(int i=1;i<=n;i++){
        b[i]=b[i-1]+a[i].p;
    }
    for(int i=1;i<=n;i++){
        c[i]=c[i-1]+b[i-1]*(a[i].x-a[i-1].x);
    }
    memset(f,63,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=i-1;j++){
            f[i]=min(f[i],f[j]+c[i]-c[j]-b[j]*(a[i].x-a[j].x)+a[i].c);
        }
    }
    for(int i=n;i>=1;i--){
        if(op==0){
            as=min(as,f[i]);
        }
        op+=a[i].c;
    }
    cout<<as<<endl;
	return 0;
}

于是,设:

g_i=f_i+ac_i+c_i-c_i+b_i*ax_i

则:

g_i=\min_{j=0}^{i-1}(g_j-b_j*ax_i)

代码:

#include<bits/stdc++.h>
using namespace std;
long long n,f[1000006],b[1000006],c[1000006],as=1e18+7,op=0;
struct one{
    long long x,p,c;
}a[1000006];
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i].x>>a[i].p>>a[i].c;
    }
    for(int i=1;i<=n;i++){
        b[i]=b[i-1]+a[i].p;
    }
    for(int i=1;i<=n;i++){
        c[i]=c[i-1]+b[i-1]*(a[i].x-a[i-1].x);
    }
    memset(f,63,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=i-1;j++){
            f[i]=min(f[i],f[j]-b[j]*a[i].x);
        }
        f[i]=f[i]+a[i].c+c[i];
        f[i]=f[i]-c[i]+b[i]*a[i].x;
    }
    for(int i=n;i>=1;i--){
        if(op==0){
            as=min(as,f[i]+c[i]-b[i]*a[i].x);
        }
        op+=a[i].p;
    }
    cout<<as<<endl;
	return 0;
}

若:

f_j-b_j*a_i>f_k-b_k*a_i\rightarrow \frac{f_j-f_k}{b_j-b_k}>a_i

所以,维护凸包,用斜率优化DP即可。

#include<bits/stdc++.h>
using namespace std;
long long n,f[1000006],b[1000006],c[1000006],as=1e18+7,op=0,dl[1000006],ld=1,rd=1;
struct one{
    long long x,p,c;
}a[1000006];
int main(){
	cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i].x>>a[i].p>>a[i].c;
    }
    for(int i=1;i<=n;i++){
        b[i]=b[i-1]+a[i].p;
    }
    for(int i=1;i<=n;i++){
        c[i]=c[i-1]+b[i-1]*(a[i].x-a[i-1].x);
    }
    memset(f,63,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++){
        long long l=ld,r=rd-1,md=rd;
        if(rd-ld+1>=2){
            while(l<=r){
                long long mid=(l+r)/2;
                if((f[dl[mid+1]]-f[dl[mid]])>=a[i].x*(b[dl[mid+1]]-b[dl[mid]])){
                    md=min(md,mid);
                    f[i]=min(f[i],f[dl[mid]]-b[dl[mid]]*a[i].x);
                    f[i]=min(f[i],f[dl[mid+1]]-b[dl[mid+1]]*a[i].x);
                    r=mid-1;
                }
                else{
                    l=mid+1;
                }
            }
        }
        f[i]=min(f[i],f[dl[md]]-b[dl[md]]*a[i].x);
        if(rd-ld<=100) for(int j=ld;j<=rd;j++){
            f[i]=min(f[i],f[dl[j]]-b[dl[j]]*a[i].x);
        }
        f[i]=f[i]+a[i].c+c[i];
        f[i]=f[i]-c[i]+b[i]*a[i].x;        
        while(rd-ld+1>=2&&(f[i]-f[dl[rd]])*(b[dl[rd]]-b[dl[rd-1]])<(f[dl[rd]]-f[dl[rd-1]])*(b[i]-b[dl[rd]])){
            rd--;
            //return 0;
        }
        //cout<<f[i]<<" "<<b[i]<<endl;
        dl[++rd]=i;
    }
    for(int i=n;i>=1;i--){
        if(op==0){
            as=min(as,f[i]+c[i]-b[i]*a[i].x);
        }
        op+=a[i].p;
    }
    cout<<as<<endl;
	return 0;
}

posted @ 2025-10-24 10:20  bz02_2023f2  阅读(2)  评论(0)    收藏  举报  来源