其他分享
首页 > 其他分享> > LUOGU 4360 [CEOI2004]锯木厂选址 斜率优化DP

LUOGU 4360 [CEOI2004]锯木厂选址 斜率优化DP

作者:互联网

title

LUOGU 4360
题目描述

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

输入输出格式
输入格式:

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

输出格式:

输出最小的运输费用。

输入输出样例
输入样例#1:

9
1 2
2 1
3 3
1 1
3 2
1 6
2 1
1 2
1 1

输出样例#1:

26

说明

样例图示
黑点为锯木厂
在这里插入图片描述
本题共有1313个测试点,每个测试点的数据范围如下
测试点15:n2001-5:n≤2001−5:n≤200;
测试点67:n10006-7:n≤10006−7:n≤1000;
测试点713:n200007-13:n≤200007−13:n≤20000;

analysis

大佬们说这道题是斜率优化入门题,所以蒟蒻来试试,没想到一试就是一下午,o(╥﹏╥)o。

好了,说下想法,看到这题,其实很直接就想到f[i]f[i]f[i]表示把锯木厂建在第iii棵树的位置的最小费用

然后就是朴素的状态转移方程了:
f[i]=sumd[j]s[j]d[i](s[i]s[j])(j&lt;i)f[i]=sum−d[j]∗s[j]−d[i]∗(s[i]−s[j])(j&lt;i)f[i]=sum−d[j]∗s[j]−d[i]∗(s[i]−s[j])(j<i)

sumsumsum表示所有树一开始全部运送的山脚下的花费,d[i]d[i]d[i]表示距离的后缀和(因为我们是从上运到下面)s[i]s[i]s[i]表示树的重量的前缀和。那么在i,ji,ji,j处修了工厂后花费就变成了总花费sumsumsum减去从jjj厂运到山脚的额外花费d[j]s[j]d[j]*s[j]d[j]∗s[j],再减去从iii厂运到山脚下的额外花费d[i](s[i]s[j])d[i]*(s[i]-s[j])d[i]∗(s[i]−s[j])。

形象的说,就是你先把jjj前面的木材运到jj厂,然后减去这些木材运到山脚的花费,再把i,ji,ji,j之间的木材运到iii厂,再减去它们到山脚的花费。

然后我们将DPDPDP方程式变形,令j,k(j&lt;k)j,k(j&lt;k)j,k(j<k)这两种决策转移到iii的时候,kkk决策更优秀,
那么就可以得到sumd[k]s[k]d[i](s[i]s[k])&lt;sumd[j]s[j]d[i](s[i]s[j])sum-d[k]*s[k]-d[i]*(s[i]-s[k])&lt;sum-d[j]*s[j]-d[i]*(s[i]-s[j])sum−d[k]∗s[k]−d[i]∗(s[i]−s[k])<sum−d[j]∗s[j]−d[i]∗(s[i]−s[j])

整理后可以得出:d[j]s[j]d[k]s[k]&lt;d[i](s[j]s[k])d[j]*s[j]-d[k]*s[k]&lt;d[i]*(s[j]-s[k])d[j]∗s[j]−d[k]∗s[k]<d[i]∗(s[j]−s[k])

然后因为斜率d[i]d[i]d[i]是随着iii的增加而变小的,所以我们根据斜率维护一个上凸壳,因为是单调的,所以用一个队列就可以了。

然后根据我调了一下午,最终不得不求助于神犇晗神才A掉此题的前史之鉴,告诉大家,式子一定要自己推,不要看了一遍,觉得会,就直接开始写代码,要把式子写在纸上,会帮很大忙!

code

不得不long longlong~longlong long版

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e4+10;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}

template<typename T>inline void write(T &x)
{
    if (!x) { putchar('0'),putchar('\n'); return ; }
    if (x<0) putchar('-'),x=-x;
    T num=0,ch[20];
    while (x) ch[++num]=x%10+48,x/=10;
    while (num) putchar(ch[num--]);
    putchar('\n');
}

int w[maxn],d[maxn],s[maxn],q[maxn],f[maxn];
inline ll getUp(int j,int k)
{
    return d[j]*s[j]-d[k]*s[k];
}

inline ll getDw(int j,int k)
{
    return s[j]-s[k];
}

int main()
{
    int n,sum=0;read(n);
    for (int i=1; i<=n; ++i) read(w[i]),read(d[i]);
    for (int i=n; i>=1; --i) d[i]+=d[i+1];
    for (int i=1; i<=n; ++i) s[i]=s[i-1]+w[i],sum+=d[i]*w[i];

    int l=1,r=1;
    q[1]=0;
    for (int i=1; i<=n; ++i)
    {
        while (l<r && getUp(q[l],q[l+1])<(ll)d[i]*getDw(q[l],q[l+1])) ++l;
        f[i]=sum-d[q[l]]*s[q[l]]-d[i]*(s[i]-s[q[l]]);
        while (l<r && getUp(q[r-1],q[r])*getDw(q[r],i)<getUp(q[r],i)*getDw(q[r-1],q[r])) --r;
        q[++r]=i;
    }

    int ans=0x7fffffff;
    for (int i=1; i<=n; ++i) ans=min(ans,f[i]);
    write(ans);
    return 0;
}

函数doubledoubledouble版

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+10;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}

template<typename T>inline void write(T &x)
{
    if (!x) { putchar('0'),putchar('\n'); return ; }
    if (x<0) putchar('-'),x=-x;
    T num=0,ch[20];
    while (x) ch[++num]=x%10+48,x/=10;
    while (num) putchar(ch[num--]);
    putchar('\n');
}

int w[maxn],d[maxn],s[maxn],q[maxn*5],f[maxn];
inline double slope(int j,int k)
{
    return 1.0*(d[j]*s[j]-d[k]*s[k])/(s[j]-s[k]);
}

int main()
{
    int n,sum=0;read(n);
    for (int i=1; i<=n; ++i) read(w[i]),read(d[i]);
    for (int i=n; i>=1; --i) d[i]+=d[i+1];
    for (int i=1; i<=n; ++i) s[i]=s[i-1]+w[i],sum+=d[i]*w[i];

    int l=1,r=1;
    q[1]=0;
    for (int i=1; i<=n; ++i)
    {
        while (l<r && slope(q[l],q[l+1])>d[i]) ++l;
        f[i]=sum-d[q[l]]*s[q[l]]-d[i]*(s[i]-s[q[l]]);
        while (l<r && slope(q[r-1],q[r])<slope(q[r],i)) --r;
        q[++r]=i;
    }

    int ans=0x7fffffff;
    for (int i=1; i<=n; ++i) ans=min(ans,f[i]);
    write(ans);
    return 0;
}

标签:ch,4360,LUOGU,sum,锯木厂,lt,iii,getchar
来源: https://blog.csdn.net/huashuimu2003/article/details/90270896