zoukankan      html  css  js  c++  java
  • BZOJ1492 货币兑换 CDQ分治优化DP

    1492: [NOI2007]货币兑换Cash

    Time Limit: 5 Sec  Memory Limit: 64 MB

    Description

    小Y最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下
    简称B券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。每天随着市场的起伏波动,
    两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 K 天中 A券 和 B券 的
    价值分别为 AK 和 BK(元/单位金券)。为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法
    。比例交易法分为两个方面:(a)卖出金券:顾客提供一个 [0,100] 内的实数 OP 作为卖出比例,其意义为:将
     OP% 的 A券和 OP% 的 B券 以当时的价值兑换为人民币;(b)买入金券:顾客支付 IP 元人民币,交易所将会兑
    换给用户总价值为 IP 的金券,并且,满足提供给顾客的A券和B券的比例在第 K 天恰好为 RateK;例如,假定接
    下来 3 天内的 Ak、Bk、RateK 的变化分别为:
    假定在第一天时,用户手中有 100元 人民币但是没有任何金券。用户可以执行以下的操作:
    注意到,同一天内可以进行多次操作。小Y是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经
    知道了未来N天内的A券和B券的价值以及Rate。他还希望能够计算出来,如果开始时拥有S元钱,那么N天后最多能
    够获得多少元钱。

    Input

    输入第一行两个正整数N、S,分别表示小Y能预知的天数以及初始时拥有的钱数。接下来N行,第K行三个实数AK、B
    K、RateK,意义如题目中所述。
    测试数据设计使得精度误差不会超过10^-7。
    对于40%的测试数据,满足N ≤10;
    对于60%的测试数据,满足N ≤1 000;
    对于100%的测试数据,满足N ≤100 000;
    对于100%的测试数据,满足:0<AK≤10;0<BK≤10;0<RateK≤100;MaxProfit≤10^9。
    【提示】
    1.输入文件可能很大,请采用快速的读入方式。
    2.必然存在一种最优的买卖方案满足:
    每次买进操作使用完所有的人民币;
    每次卖出操作卖出所有的金券。
     

    Output

    只有一个实数MaxProfit,表示第N天的操作结束时能够获得的最大的金钱数目。答案保留3位小数。

    Sample Input

    3 100
    1 1 1
    1 2 2
    2 2 3

    Sample Output

    225.000

    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
    ",ans[n]);
    64 }
    nlogn
     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
    ",ans[n]);
    60 }
    nlog^2n
  • 相关阅读:
    insert into output使用
    内插字符串$与复合格式设置{}
    网站连接数据库连接不上原因是ip地址与端口号格式不对
    IIS中的MIME类型设置
    select distinct
    复制表备份表的问题
    一些碎知识
    题解【洛谷P3574】[POI2014]FAR-FarmCraft
    题解【洛谷P6029】[JSOI2010]旅行
    题解【BZOJ4472】[JSOI2015]salesman
  • 原文地址:https://www.cnblogs.com/LadyLex/p/8028556.html
Copyright © 2011-2022 走看看