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
  • 相关阅读:
    环境是如何建立的 启动文件有什么
    环境中存储的是什么
    串行 并行 异步 同步
    TPC-H is a Decision Support Benchmark
    进程通信类型 管道是Linux支持的最初Unix IPC形式之一 命名管道 匿名管道
    删除环境变量
    14.3.2.2 autocommit, Commit, and Rollback 自动提交 提交和回滚
    14.3.2.2 autocommit, Commit, and Rollback 自动提交 提交和回滚
    14.3.2.1 Transaction Isolation Levels 事务隔离级别
    14.3.2.1 Transaction Isolation Levels 事务隔离级别
  • 原文地址:https://www.cnblogs.com/LadyLex/p/8028556.html
Copyright © 2011-2022 走看看