BZOJ1492 货币兑换 CDQ分治优化DP
Posted LadyLex
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ1492 货币兑换 CDQ分治优化DP相关的知识,希望对你有一定的参考价值。
1492: [NOI2007]货币兑换Cash
Time Limit: 5 Sec Memory Limit: 64 MBDescription
Input
对于40%的测试数据,满足N ≤10;
对于60%的测试数据,满足N ≤1 000;
对于100%的测试数据,满足N ≤100 000;
Output
只有一个实数MaxProfit,表示第N天的操作结束时能够获得的最大的金钱数目。答案保留3位小数。
Sample Input
1 1 1
1 2 2
2 2 3
Sample Output
HINT
(转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/8028556.html )
终于把CDQ维护凸壳学了……
那么既然题目已经给提示2了,我们就可以针对性的设$f[i]$为第i天获得的最多A券数,$ans[i]$为第i天的最大获利,那么我们要求的就是ans[n]了。
这个dp显然是可以$O(n^{2})$解决的……但是这样拿不了后面的分数。
然后你就想啊,肯定要优化啊……然后你就可以化出一个斜率的式子来。
对于$ans[i]$的决策,我们设两天j和k,j比k优秀的话,就会有
$(f[j]-f[k])*a[i]+(f[j]/rate[j]-f[k]/rate[k])*b[i]>0$
再设$g[i]=f[i]/rate[i]$(也就是第i天获得的最多B券数),为了处理不等式的符号我们设$f[j]<f[k]$
所以有:
$(g[j]-g[k])*b[i]>-(f[j]-f[k])*a[i]$
$(g[j]-g[k])/(f[j]-f[k])<-a[i]/b[i]$
(当然,实际情况是有$f[j]==f[k]$,即斜率不存在的情况存在的,到时候还要讨论。)
那么我们转化到一些坐标为(f[i],g[i])的二维平面的点上来。
我们建立一个这些点的上凸壳,然后在凸壳上二分最靠左的最后一个满足$k(point(x),point(x+1))<-a[i]/b[i]$的点x,
那么$x+1$就是最优秀的取值,也即本次决策点。
但是你发现这个f[i]不随i单调……那么我们考虑splay或者CDQ
打个J的splay啊
那么我们CDQ维护凸壳并且决策就好了……
具体实现是让$f[i]$有序之后按照正常方法建凸壳,然后让$-a[i]/b[i]$有序(我是从大到小排序),单调扫一边完成决策。
怎么让这俩有序呢……一是大力sort,复杂度$O(nlog^{2}n)$,一是归并排序,复杂度$O(nlogn)$
两种方法我都打了一下……感觉少个$logn$没快到哪去,也没长到哪去……
如果复杂度可行还是打$log^{2}$吧……
两份代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 #define eps 1e-8 6 #define N 100010 7 #define db double 8 #define inf 0x7fffffff 9 #define sign(a) (((a)>-eps)-((a)<eps)) 10 int n,top,sta[N],id[N],tmp[N],idk[N],tmpk[N],match[N]; 11 db ans[N],ak[N],bk[N],rate[N],f[N],g[N]; 12 inline db max(db a,db b){return a>b?a:b;} 13 inline bool comp(const int &a,const int &b) 14 {return f[a]<f[b] || ( sign(f[a]-f[b])==0&&g[a]<g[b] );} 15 inline double k(int a,int b) 16 { 17 if(sign(f[a]-f[b])==0)return sign(g[a]-g[b])*inf; 18 return (g[a]-g[b])/(f[a]-f[b]); 19 } 20 inline bool compk(const int &a,const int &b) 21 {return sign( (-ak[a]/bk[a]) - (-ak[b]/bk[b]) ) >0 ;} 22 inline void CDQ(int l,int r) 23 { 24 if(l==r){g[l]=f[l]/rate[l];return;} 25 register int hd1,i,t,mi=l+r>>1,p=l-1,q=mi,h=l-1; 26 for(i=l;i<=r;++i) 27 if(match[idk[i]]<=mi)tmpk[++p]=idk[i]; 28 else tmpk[++q]=idk[i]; 29 for(i=l;i<=r;++i)idk[i]=tmpk[i]; 30 CDQ(l,mi); 31 for(top=0,i=l;i<=mi;++i) 32 { 33 while(top>1 && k(sta[top-1],sta[top]) < k(sta[top],id[i]) )--top; 34 sta[++top]=id[i]; 35 } 36 for(hd1=1,i=mi+1;i<=r;++i) 37 { 38 t=match[idk[i]]; 39 while( hd1<top&&sign( k(sta[hd1],sta[hd1+1]) - (-ak[t]/bk[t]) ) >=0 )++hd1; 40 ans[t]=max(ans[t],f[sta[hd1]]*ak[t]+g[sta[hd1]]*bk[t]); 41 } 42 for(i=mi+1;i<=r;++i) 43 t=match[idk[i]],ans[t]=max(ans[t],ans[t-1]),f[t]=ans[t]*rate[t]/(ak[t]*rate[t]+bk[t]); 44 CDQ(mi+1,r); 45 p=l,q=mi+1,h=l; 46 while(p<=mi&&q<=r) 47 if(comp(id[p],id[q]))tmp[h++]=id[p++]; 48 else tmp[h++]=id[q++]; 49 while(p<=mi)tmp[h++]=id[p++]; 50 while(q<=r)tmp[h++]=id[q++]; 51 for(i=l;i<=r;++i)id[i]=tmp[i]; 52 } 53 int main() 54 { 55 register int i,j; 56 scanf("%d%lf",&n,&ans[1]); 57 for(i=1;i<=n;++i) 58 scanf("%lf%lf%lf",&ak[i],&bk[i],&rate[i]); 59 f[1]=ans[1]*rate[1]/(ak[1]*rate[1]+bk[1]); 60 for(i=1;i<=n;++i)id[i]=idk[i]=match[i]=i; 61 sort(match+1,match+n+1,compk); 62 CDQ(1,n); 63 printf("%.3f\\n",ans[n]); 64 }
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 #define eps 1e-8 6 #define N 100010 7 #define db double 8 #define inf 0x7fffffff 9 #define sign(a) (((a)>-eps)-((a)<eps)) 10 int n; 11 db ans[N],ak[N],bk[N],rate[N],f[N],g[N]; 12 inline db max(db a,db b){return a>b?a:b;} 13 int top,sta[N]; 14 int id[N],tmp[N],idk[N]; 15 inline bool comp(const int &a,const int &b) 16 { 17 return f[a]<f[b] || ( sign(f[a]-f[b])==0&&g[a]<g[b] ); 18 } 19 inline double k(int a,int b) 20 { 21 if(sign(f[a]-f[b])==0) 22 return sign(g[a]-g[b])*inf; 23 return (g[a]-g[b])/(f[a]-f[b]); 24 } 25 inline bool compk(const int &a,const int &b) 26 { 27 return sign( (-ak[a]/bk[a]) - (-ak[b]/bk[b]) ) >0 ; 28 } 29 inline void CDQ(int l,int r) 30 { 31 if(l==r){g[l]=f[l]/rate[l];return;} 32 register int hd1,i,mi=l+r>>1; 33 CDQ(l,mi); 34 for(i=mi+1;i<=r;++i)id[i]=i;sort(id+l,id+mi+1,comp); 35 for(i=mi+1;i<=r;++i)idk[i]=i;sort(idk+mi+1,idk+r+1,compk); 36 for(top=0,i=l;i<=mi;++i) 37 { 38 while(top>1 && k(sta[top-1],sta[top]) < k(sta[top],id[i]) )--top;//**** 39 sta[++top]=id[i]; 40 } 41 for(hd1=1,i=mi+1;i<=r;++i) 42 { 43 while( hd1<top&&sign( k(sta[hd1],sta[hd1+1]) - (-ak[idk[i]]/bk[idk[i]]) ) >=0 )++hd1; 44 ans[idk[i]]=max(ans[idk[i]],f[sta[hd1]]*ak[idk[i]]+g[sta[hd1]]*bk[idk[i]]); 45 } 46 for(i=mi+1;i<=r;++i) 47 ans[i]=max(ans[i],ans[i-1]),f[i]=ans[i]*rate[i]/(ak[i]*rate[i]+bk[i]); 48 CDQ(mi+1,r); 49 } 50 int main() 51 { 52 register int i,j; 53 scanf("%d%lf",&n,&ans[1]); 54 for(i=1;i<=n;++i) 55 scanf("%lf%lf%lf",&ak[i],&bk[i],&rate[i]); 56 f[1]=ans[1]*rate[1]/(ak[1]*rate[1]+bk[1]); 57 for(i=1;i<=n;++i)id[i]=i; 58 CDQ(1,n); 59 printf("%.3f\\n",ans[n]); 60 }
以上是关于BZOJ1492 货币兑换 CDQ分治优化DP的主要内容,如果未能解决你的问题,请参考以下文章
bzoj1492[NOI2007]货币兑换Cash cdq分治+斜率优化dp
[BZOJ 1492][NOI2007]货币兑换Cash(CDQ分治+斜率优化Dp)
bzoj 1492 [NOI2007]货币兑换Cash(斜率dp+cdq分治)
[BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)