NKOJ-1918 锯木厂选址

10 篇文章 1 订阅
5 篇文章 0 订阅

NKOJ-1918 锯木厂选址

由于不想写计算几何 所以选择做斜率优化…

话不多说 我们来讲讲斜率优化这个东西

所谓斜率优化 其实有点类似于单调队列
只不过它是用来优化DP的
具体的实现方式 其实就是通过将某些变量之间的关系给二维化
通过二维平面中的点与点之间 线的斜率 来表示这个方案是否更优

具体拿这道题来讲=

首先请保证你熟悉了这道题 不然下面的推导可能会看着很恐怖

开始了

假设当前的点为 i
i1 i 的距离为 d[i]
从起点到 i 的距离为 Sd[i] ( d[i] 的前缀和)
从起点到 i 一共要运的木材为 S[i] (包括 i 号点的木材)
从起点到 i 一共要运的木材的花费为 C[i]
j ( i 之前任意点) 到 i 的木材全部运到 i 的费用为 F(j,i)

于是有

F(j,i)=C[i]C[j1]S[j1](Sd[i]Sd[j1])

假设任意方案的总费用为 RES
RES=C[a]+W(a+1,b)+W(b+1,n+1)

表示在 abn+1 处建厂的总费用(当然 n+1 就是山脚那个不能动的场子)

那么讨论点 i
i 处建厂 并且在 i 之前的某一处 j 建第二个厂
我们希望找到 j 使得当前的 RES 最小

那么讨论 j=a j=b>a
b 处建厂 比 a 处建厂更优
则有

RES(a,i)>RES(b,i)
(括号内的变量为建厂位置,即原 RES 表达式中的 ab )

那么

C[a]+F(a+1,i)+F(i+1,n+1)>C[b]+F(b+1,i)+F(i+1,n+1)

C[a]+F(a+1,i)>C[b]+F(b+1,i)

按照 F(a,b) 展开
C[a]+C[i]C[a]S[a](Sd[i]Sd[a])>C[b]+C[i]C[b]S[b](Sd[i]Sd[b])

化简
S[a]Sd[a]S[a]Sd[i]>S[b]Sd[b]S[b]Sd[i]

S[a]Sd[a]S[b]Sd[b]>(S[a]S[b])Sd[i]

因为 b>a 所以 S[a]<S[b] (十分显然的单调性)
因此移项
S[a]Sd][a]S[b]Sd[b]S[a]S[b]<Sd[i]

因此 对于固定的 i 来讲
a<b<i 且满足上式 那么显然在 b 建厂更加理想
并且对于每一个 i 最合适的 b 有且仅有1个

大概证明(不严格)
对于 把厂从 a 搬到 b
0>b 的费用 比 0>a 的费用大
b>i 的费用 比 a>i 的费用小
前后单调性加一加
差不多一个类似于二次函数的曲线的图像
最优点只有一个

同时 若 b 相比于 a 对于 i 更优
那么 由于 Sd[i] 单调递增(起码不递减)
所以 Sd[i]<Sd[i1]
就有

S[a]Sd][a]S[b]Sd[b]S[a]S[b]<Sd[i]<Sd[i+1]

因此对于 i 更优的 对于i+1一定更优

因此维护一个单调队列
对于一个 i
删除队首不满足最优解的点
删除队尾不满足最优解的点
然后再加入 i
并更新答案(用队首的点和 i <script id="MathJax-Element-11695" type="math/tex">i</script> 点建厂)

大概意思就是把这种图
这里写图片描述
优化成这种图
这里写图片描述

附上代码

#include <iostream>
#include <cstdio>
using namespace std;

int n,Ans=2000000001;
int d[20123],Sd[20123],S[20123],C[20123];

int q[20005];

double Slpoe(int j,int k){return (S[j]*Sd[j]-S[k]*Sd[k])*1.0/(S[j]-S[k]);}

int main()
{
    //freopen("In.txt","r",stdin);
    scanf("%d",&n);
    for(int x,i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&d[i]);
        S[i]=S[i-1]+x;
        C[i]=C[i-1]+S[i-1]*d[i-1];
        Sd[i]=Sd[i-1]+d[i-1];
    }
    S[n+1]=S[n];
    Sd[n+1]=Sd[n]+d[n];
    C[n+1]=C[n]+S[n]*d[n];
    int l=1,r=1;
    q[1]=0;
    for(int j,i=1;i<=n;i++)
    {
        while(l<r&&Slpoe(q[l],q[l+1])<Sd[i])l++;
        j=q[l];
        Ans=min(Ans,C[n+1]-S[j]*(Sd[i]-Sd[j])-S[i]*(Sd[n+1]-Sd[i]));
        while(l<r&&Slpoe(q[r-1],q[r])>Slpoe(q[r],i))r--;
        q[++r]=i;
    }
    printf("%d",Ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值