由于不想写计算几何 所以选择做斜率优化…
话不多说 我们来讲讲斜率优化这个东西
所谓斜率优化 其实有点类似于单调队列
只不过它是用来优化DP的
具体的实现方式 其实就是通过将某些变量之间的关系给二维化
通过二维平面中的点与点之间 线的斜率 来表示这个方案是否更优
具体拿这道题来讲=
首先请保证你熟悉了这道题 不然下面的推导可能会看着很恐怖
开始了
假设当前的点为
i
从
从起点到
i
的距离为
从起点到
i
一共要运的木材为
从起点到
将
j
(
于是有
假设任意方案的总费用为 RES
表示在 a、b、n+1 处建厂的总费用(当然 n+1 就是山脚那个不能动的场子)
那么讨论点
i
在
我们希望找到
j
使得当前的
那么讨论
j=a
和
j=b>a
若
b
处建厂 比
则有
那么
按照 F(a,b) 展开
化简
因为 b>a 所以 S[a]<S[b] (十分显然的单调性)
因此移项
因此 对于固定的
i
来讲
若
并且对于每一个
大概证明(不严格)
对于 把厂从
b−>i
的费用 比
a−>i
的费用小
前后单调性加一加
差不多一个类似于二次函数的曲线的图像
最优点只有一个
同时 若
b
相比于
那么 由于
所以
Sd[i]<Sd[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;
}