sol
我觉得我还是要来复习一下斜率优化
首先我们要写出(O(n^2))的(DP)式子
设(f_i)表示解决前(i)个工厂的产品,且一定要在第(i)个工厂建设仓库的最小费用
[f_i=min{f_j+sum_{j+1le l le i}p_l*(x_i-x_l)}+C_i(1le j<i)
]
设(sum_i)表示(p_i)的前缀和,(tot_i)表示(x_i*p_i)的前缀和。那么上式可以简化为
[f_i=min{f_j+x_i(sum_i-sum_j)-(tot_i-tot_j)}+C_i\=min{f_j-x_i*sum_j+tot_j}+x_isum_i-tot_i+C_i(1le j<i)
]
我们假设现在有两个决策(j)和(k),其中(k<j),且选(j)比选(k)更优。所以
[f_j-x_isum_j+tot_j < f_k-x_isum_k+tot_k
]
移一下项
[frac {f_j+tot_j-f_k-tot_k}{sum_j-sum_k}<x_i
]
左边那个式子像是什么?斜率?
对于每一个决策点,视(x_i=sum_i,y_i=f_i+tot_i),那么每个决策就可以对应二维平面上的一个点
对于相邻的两个决策点,如果满足斜率(<x_i),那么(k)就不是那个更优的。而因为(x_i)单调递增,所以这样(k)就永远不能成为最优决策点。所以就可以直接把(k)这个决策点弹掉。
用单调队列把时间复杂度优化到(O(n))
code
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1000005;
#define ll long long
ll gi()
{
ll x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
int n,q[N],hd,tl;
ll x[N],P[N],C[N],sum[N],tot[N],f[N];
double calc(int j,int k)
{
return (double)(f[j]+tot[j]-f[k]-tot[k])/(1.0*(sum[j]-sum[k]));
}
int main()
{
n=gi();
for (int i=1;i<=n;i++)
{
x[i]=gi();P[i]=gi();C[i]=gi();
sum[i]=sum[i-1]+P[i];
tot[i]=tot[i-1]+P[i]*x[i];
}
for (int i=1,j;i<=n;i++)
{
while (hd<tl&&calc(q[hd],q[hd+1])<x[i]) ++hd;
j=q[hd];//j是此时的最优决策点
f[i]=f[j]-x[i]*sum[j]+tot[j]+x[i]*sum[i]-tot[i]+C[i];
while (hd<tl&&calc(q[tl-1],q[tl])>calc(q[tl],i)) --tl;
q[++tl]=i;
}
printf("%lld
",f[n]);
return 0;
}