LUOGU4360[CEOI2004]锯木厂选址——斜率优化dp

题目描述

从山顶上到山底下沿着一条直线种植了 nn 棵老树。当地的政府决定把他们砍下来。为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂。木材只能朝山下运。山脚下有一个锯木厂。另外两个锯木厂将新修建在山路上。你必须决定在哪里修建这两个锯木厂,使得运输的费用总和最小。假定运输每公斤木材每米需要一分钱你的任务是编写一个程序,从输入文件中读入树的个数和他们的重量与位置,计算最小运输费用。

输入格式:
输入的第一行为一个正整数n ——树的个数 (2≤n≤20000)。树从山顶到山脚按照 1,2,…,n标号。
接下来 n 行,每行有两个正整数(用空格分开)。
第 i+1行含有: w_i——第 i 棵树的重量(公斤为单位)和 d_i​——第i棵树和第 i+1 棵树之间的距离, 1≤w_i≤10000,0≤d_i≤10000
最后一颗树的 d_n,表示第 n 棵树到山脚的锯木厂的距离。保证所有树运到山脚的锯木厂所需要的费用小于 2×10^9分。

输出格式:
输出最小的运输费用。

输入样例#1:
9
1 2
2 1
3 3
1 1
3 2
1 6
2 1
1 2
1 1
输出样例#1:
26
说明
样例图示

黑点为锯木厂
这里写图片描述
本题共有 13 个测试点,每个测试点的数据范围如下

测试点 1-5:n≤200;

测试点 6-7:n≤1000 ;

测试点 7-13:n≤20000 ;


我们来考虑这道题的正解,首先我们可以得出最基本的dp方程:f[i][j]表示到i点,已经建了i个站的最小花费
//我们先预处理出f[i][1]
f[i][2]=min(f[i][2],f[k][1]+∑cost[j]) //(j∈[k,i]),cost[j]表示点j从k点到i的花费。
最简单的思想就是去N^3的处理这个东西。然后恭喜你拿了5个点的分。
然后我们考虑进行斜率优化。首先就是我们要处理出f[i][1]这个东西。f[i][1]实际上就等于f[i-1][1]的值加上点1~i-1一起移动dist[i-1](dist[i-1]表示从i-1到i的路程)的值。f[i][1]=f[i-1][1]+sum[i-1]*dist[i-1];
然后我们处理∑cost[j]这个东东。cost[j]=val[j]*(rou[j]-rou[i]) 其中val[j]表示j点的树的权值,而rou[i]表示的是从i到山底的距离。然后我们要处理∑。我们用sum[i]表示从树1到i的权值前缀和,则
//我们令mult[i]=val[i]*rou[i],all[i]为mul[i]的前缀和,则
∑cost[j]=all[i]-all[j]-sum[i]*rou[i]+sum[j]*rou[j]
所以我们有了最终的DP方程:
f[i][2]=f[j][1]+all[i]-all[j]-sum[i]*rou[i]+sum[j]*rou[i]
f[i][2]-rou[i]*sum[j]=f[j][1]+all[i]-all[j]-sum[i]*rou[i]
  b   +   k   *  x   =             y
我们的斜率是-rou[i],是单调递增的,所以我们维护一个下凸包。
#include<bits/stdc++.h>
#define MAXN 20005
#define ll long long
using namespace std;
ll read(){
    char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll n,r,w,ans,f[MAXN][3],q[MAXN],dist[MAXN],sum[MAXN],val[MAXN],rou[MAXN],mult[MAXN],all[MAXN];
double y(ll i){return f[i][1]-all[i];}
double x(ll i){return sum[i];}
double k(ll u,ll v){
    return (double)(y(v)-y(u))/(x(v)-x(u));
}
int main()
{
    n=read();
    for(ll i=1;i<=n;i++){
        val[i]=read();dist[i]=read();
        sum[i]=sum[i-1]+val[i];
    }
    for(ll i=n;i>=1;i--) rou[i]=rou[i+1]+dist[i];
    r=w=0;ans=2e9;
    for(ll i=1;i<=n;i++){
        mult[i]=val[i]*rou[i];all[i]=all[i-1]+mult[i];
        f[i][1]=f[i-1][1]+sum[i-1]*dist[i-1];
    }
    for(ll i=1;i<=n;i++){
        while(r<w&&-rou[i]>k(q[r],q[r+1])) r++;
        ll j=q[r];f[i][2]=f[j][1]+all[i]-all[j]-sum[i]*rou[i]+sum[j]*rou[i];
        while(r<w&&k(q[w-1],q[w])>k(q[w],i)) w--;
        q[++w]=i;
    }
    for(ll i=1;i<=n;i++){
        ans=min(ans,f[i][2]+all[n]-all[i]);
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/stevensonson/article/details/80289708
今日推荐