BZOJ 1096 [ZJOI2007]仓库建设 BZOJ 3437 小P的牧场 BZOJ 3156 防御准备 斜率优化dp
[ZJOI2007]仓库建设 >原题链接<
小P的牧场 >原题链接<
防御准备 >原题链接<
由于是三题,就不放题面了
思路 :
BZOJ1096:本题Dp的方程比较好推导,我们设两个sum数组分别对货物量进行前缀求和、sum1对前i-1个仓库的货物都运到i的代价进行求和
设F[i]为在i点建仓库的总最小花费。
那么Dp方程显然为F[i]=min{ f[j] + sum1[i] - sum1[j] - sum[j] * ( x[i] - x[j] ) + c[i] } 考虑斜率优化 。 对本式进行化简得
F[i]-c[i] - sum1[i] = F[j] - sum1[j] - sum[j] * x[j] + s[j] * x[i] ;
我们设
B(j) = F[j] + x[j] * s[j] -sum1[j]
K(j)= -sum[j];
Y (i,j) = K(j)* x[i] + B(j) = F[i] - c[i] - sum1[i]
故F[i] = Y(i,j) + sum1[i] + c[i]
考虑单调性 K(j) 单调递减 x[i]单调递增 若队尾和I构成的斜率斜率小于队尾二号和i构成的斜率,则弹出队尾;
考虑队首元素时,若Y(队首) >= Y(队首2号元素) 则弹出队首 。
而BZOJ 3437 只是把x[i]变成了i,其他的没有变化
而BZOJ 3156 只是在上题的基础上把sum[i]变成i,其他的没有变化。
附上1096的代码
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1200000; #define int long long #define ll long long #define K(i) (-s[i]) #define B(i) (f[i]+x[i]*s[i]-s1[i]) #define Y(i, j) (K(j)*x[i]+B(j)) int x[N], p[N], c[N], s[N], s1[N], f[N]; int q[N], l, r; bool cmp(int i,int j,int k) { ll aslhkdfljkashdfkljsahdkljfhasdkjf=(K(i)-K(k))*(B(j)-B(i)); ll y=(K(i)-K(j))*(B(k)-B(i)); return aslhkdfljkashdfkljsahdkljfhasdkjf>=y; } #undef int int main() { int n, i; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%lld%lld%lld",&x[i],&p[i],&c[i]); s[i]=s[i-1]+p[i]; s1[i]=s1[i-1]+s[i-1]*(x[i]-x[i-1]); } for(i=1;i<=n;i++) { while(l<r&&Y(i,q[l])>=Y(i,q[l+1])) l++; f[i]=Y(i,q[l])+s1[i]+c[i]; while(l<r&&cmp(i,q[r-1],q[r]))r--; q[++r]=i; } printf("%lld\n",f[n]); }
欢迎来原博客看看 >原文链接<