【BZOJ】【1096】【ZJOI2007】仓库建设

DP/斜率优化


  Orz Hzwer

八中好像挂了……明天再提交吧……

UPD:2015-03-12 17:24:43

  算了,毕竟是第一道题,还是仔细写一下斜率优化的过程吧。(部分引自Hzwer的题解)

  首先我们根据题意可以列出动规方程 $$ f[i]=min{ f[j]+cal(j,i) }$$

  处理$cal(j,i)$可以利用前缀和的思想,令$ sum[i]=sum_{k=1}^{i} p[k] $

  对于物品$1 ~ i$,如果都从$0$运到$i$,则费用为$(sum[i]-sum[j])*x[i]$

  但由于物品的起始点不都在0,所以对于每个物品$k$可以少花费$x[k]*p[k]$

  定义$b[i]=sum_{k=1}^{i} (x[k]*p[k]) $

  可得 $ f[i]=min{ f[j]+(sum[i]-sum[j])*x[i]-(b[i]-b[j])+c[i] } $

  下面证明决策单调性:

    如果$ j > k $ 且$j$比$k$更优,则有:

[ egin{aligned} f[j]+(sum[i]-sum[j])*x[i]-(b[i]-b[j])+c[i] &< f[k]+(sum[i]-sum[j])*x[i]-(b[i]-b[k])+c[i] \ f[j]-f[k]+b[j]-b[k] &< (sum[j]-sum[k])*x[i] \ frac{f[j]-f[k]+b[j]-b[k]}{sum[j]-sum[k]} &< x[i] end{aligned} ]

  至于为什么要证这个东西请看论文:《动态规划的斜率优化》

  嗯我们现在就知道了对于每个状态$i$,从1 ~ i-1这些决策中的“当前最优决策”是有一个单调性的!比如我们从1开始枚举到 k ,发现 k 是一个最优决策可以更新答案f[i],然后我们继续枚举直到决策 j ,满足上面那个不等式!则表明决策 j 比决策 k 更优!那么有什么用呢?我们根据不等式发现,这个“更优”的属性,只跟 j 和 k 有关,与阶段 i 是无关的!也就是说,当 j 成为一个可选的方案的时候,k 就永远也不用再考虑它了,这就大大减少了转移时的决策数!从而降低了复杂度!

  说的好像很厉害……那具体操作的时候怎么操作呢?

  我们用一个队列q来维护一个斜率单调的决策序列:

 如果slop(q[l],q[l+1])<x[i](参考上面推出的不等式)则说明q[l+1]这个决策比q[l]这个决策更优,所以l++舍弃队首

 算出dp[i]

 如果slop(q[r-1],q[r])>slop(q[r],i),则队尾处不满足单调,所以要弹队尾直到满足单调性(联系凸壳的图形想想)

 插入决策 i 到队列尾部

  当然如果换了一道求max的题,则不等号方向是要改变的……

 1 /**************************************************************
 2     Problem: 1096
 3     User: Tunix
 4     Language: C++
 5     Result: Accepted
 6     Time:1784 ms
 7     Memory:52052 kb
 8 ****************************************************************/
 9  
10 //BZOJ 1096
11 #include<cstdio>
12 #include<iostream>
13 #include<algorithm>
14 #define rep(i,n) for(int i=0;i<n;++i)
15 #define F(i,j,n) for(int i=j;i<=n;++i)
16 #define D(i,j,n) for(int i=j;i>=n;--i)
17 #define pb push_back
18 using namespace std;
19 int getint(){
20     int v=0,sign=1; char ch=getchar();
21     while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();}
22     while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();}
23     return v*sign;
24 }
25 const int N=1000010;
26 typedef long long LL;
27 /******************tamplate*********************/
28 int n,l,r,q[N];
29 LL p[N],x[N],c[N],f[N],b[N],sp[N];
30 inline double slop(int k,int j){
31     return double(f[j]-f[k]+b[j]-b[k])/double(sp[j]-sp[k]);
32 }
33 int main(){
34     int n=getint();
35     F(i,1,n){
36         x[i]=getint(); p[i]=getint(); c[i]=getint();
37         sp[i]=sp[i-1]+p[i]; b[i]=b[i-1]+p[i]*x[i];
38     }
39     F(i,1,n){
40         while(l<r && slop(q[l],q[l+1])<x[i]) l++;
41         int t=q[l];
42         f[i]=f[t]-b[i]+b[t]+(sp[i]-sp[t])*x[i]+c[i];
43         while(l<r && slop(q[r-1],q[r])>slop(q[r],i))r--;
44         q[++r]=i;
45     }
46     printf("%lld
",f[n]);
47     return 0;
48 }
View Code
原文地址:https://www.cnblogs.com/Tunix/p/4331099.html