仓库建设
终于有时间写写博客了!
听说这题是NOI/CTSC的哇!
激动!先看题意!
很熟悉吧,这是一道动态规划的题:若要求从1~N的货物全部都安置的最小费用,就要用1~N-1的最小费用去更新……这就转化为了子问题的求解典型的DP。
根据子问题的定义,我们可以定义DP方程:
dp[ i ]表示在i这个地方建设仓库并且将 i 之前的所有货物都处理完的最小费用值。
由于货物只能往山下运送,那么也就是说 k 处的货物只能向 k~N 运送安置,可以枚举 j ——上一个建设仓库的地方,那么从 j+1到 i 这一段都将要安置到在 i 这个地方建设的仓库中,费用为 sigma(j+1->i) {P[k]*(X[i]-X[k])} 。
得到DP方程:
dp[ i ]=min{ ( dp[ i ] , dp[ j ]+ sigma(k=j+1->i) { P[k]*(X[i] - X[k])}) }
枚举 i 和 j 复杂度近似O( n^2 ),而计算费用的和需要O( n ),那么总复杂为O( n^3 ),爆炸!
我们可以将式子整理一下:
sigma(k=j+1->i) { P[k]*(X[i] - X[k])}) = X[ i ]*sigma( k=j+1->i ){ P[k] } - sigma( k=j+1->i ){ P[k]*X[k] }
看出来了吗,这样我们就不需要for一遍去求安置费用了,我们可以记录两个前缀和:
1、sum1[ i ]:sum1[ i ]=sum1[ i - 1 ]+P[ i ]
2、sum2[ i ]:sum2[ i ]=sum2[ i - 1 ]+P[ i ]*X[ i ]
那么全新的方程:
dp[ i ] = dp[ pos ]+x[ i ]*(sum1[ i ]-sum1[ pos ])-(sum2[ i ]-sum2[ pos ])+C[ i ],其中pos表示最优位置
这样复杂度降到了O( n^2 ),然而并没有卵用……
进行斜率优化:
假设 k>g,而且 k 处建设上一个仓库的费用比在 g 建设上一个仓库的费用优(小):
dp[ k ]+x[ i ]*(sum1[ i ]-sum1[ k ])-(sum2[ i ]-sum2[ k ])+C[ i ] —— 1式
dp[ g ]+x[ i ]*(sum1[ i ]-sum1[ g ])-(sum2[ i ]-sum2[ g ])+C[ i ] ——2式
由于 k 比 g 优,那么 1式 <= 2式!
整理一下,可以将式子中相同的都抵消掉,得到优化方程,即满足 k 比 g 优时的式子:
(dp[ k ]-dp[ j ]+sum2[ k ]-sum2[ j ]) / (sum1[ k ]-sum1[ j ]) <= X[ i ],我们把不等式左边记为斜率
维护一个凸包(斜率递增),并且使用队列存储符合凸包的点,那么每一次碰到新的 i 时,我们只需要比较X[ i ]和队首和队首+1的形成的斜率的值(k=head+1 , g=head),如果小于X[ i ],那么说明head的费用一定不比head+1优!这时我们可以将head删掉,将head+1作为新的队首;否则说明当前 i 下,head比head+1优,不删。注意,需要保证队列之中有东西
最后得到的队首一定是对于当前 i 来说一定是最优的,那么用之更新!
这时候就入队吗?不!
更新完毕,我们还是需要维护一个凸包,也就是队列尾部:tail 以及 tail-1 和将要入队的 i 必须保证:
tail与tail-1构成的斜率小于tail与i构成的斜率!
好了,这就可以将 i 入队了!
最后的答案就是dp[ N ]
AC
下面放程序:
#include<bits/stdc++.h>
const int N=1000000+5;
using namespace std;
long long dp[N],sum1[N],sum2[N],x[N],p[N],c[N];
int n,h,t,q[N<<4];
double slope(int j,int k){
return (double)(dp[k]-dp[j]+sum2[k]-sum2[j])/(double)(sum1[k]-sum1[j]);
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
cin>>x[i]>>p[i]>>c[i];
for(int i=1;i<=n;i++){
sum1[i]=sum1[i-1]+p[i];
sum2[i]=sum2[i-1]+p[i]*x[i];
}
for(int i=1;i<=n;i++){
while(h<t && slope(q[h],q[h+1])<=x[i])
h++;
int tmp=q[h];
dp[i]=dp[tmp]+x[i]*(sum1[i]-sum1[tmp])-(sum2[i]-sum2[tmp])+c[i];
while(h<t && slope(q[t-1],q[t])>slope(q[t],i))
t--;
q[++t]=i;
}
cout<<dp[n];
return 0;
}
呼!OK,返校!