zoukankan      html  css  js  c++  java
  • BZOJ_1492_[NOI2007]货币兑换Cash_CDQ分治+斜率优化

    BZOJ_1492_[NOI2007]货币兑换Cash_CDQ分治+斜率优化

    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,意义如题目中所述。对于100%的测试数据,满足:0<AK≤10;0<BK≤10;0<RateK≤100;MaxProfit≤1
    0^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


    首先贪心的想,每天的操作只有三种可能,什么也不干,全部买入和全部卖出。

    设x[i]为第i天能获得A卷的数量,y[i]为第i天能获得B卷的数量。

    y[i]=f[i]/(rate[i]*A[i]+B[i]),x[i]=y[i]*rate[i]。

    那么f[i]=max(f[i-1],x[j]*A[i]+y[j]*B[i]),其中我们需要找到一个已经确定F值得j来转移这个过程。

    F[i]=X[j]*A[i]+Y[j]*B[i]

    F[i]/A[i]=Y[j]*(B[i]/A[i])+X[j]。

    把Y[j]当做斜率。那么我们会发现不仅Y[j]不单调,每次切的横坐标(B[i]/A[i])也不单调。

    这时候我们使用CDQ分治。

    本题的思想:对左边进行处理,处理左边对右边的影响(F的转移),再对右边进行处理。

    先按(B[i]/A[i])排序,然后每步再按斜率归并上去。

    也就是说我们到了一个局面,满足左边的斜率单调,右边每次切的横坐标单调。

    这个就很好办了,对左边单调栈求凸包,然后维护一个指针扫一遍凸包。

    正确性:所有能更新F[i]的F[j]在更新F[i]之前都已经更新完毕了,同时F[i]被1~i-1中的每个F[j]都更新到了。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    typedef double f2;
    #define N 100050
    #define eps 1e-6
    f2 xx[N],yy[N],f[N],A[N],B[N],rate[N],K[N],pos[N];
    int n,t[N],tmp[N],S[N];
    #define Y(i,j) (K[j]*pos[i]+xx[j])
    bool cmp(int x,int y) {return pos[x]<pos[y];}
    bool judge(int p1,int p2,int p3) {
        return (yy[p2]-yy[p3])*(xx[p1]-xx[p2])<=(yy[p1]-yy[p2])*(xx[p2]-xx[p3]);
    }
    void solve(int l,int r) {
        if(l==r) {
            f[l]=max(f[l],f[l-1]);
            yy[l]=f[l]/(rate[l]*A[l]+B[l]);
            xx[l]=yy[l]*rate[l];
            K[l]=yy[l];
            return ;
        }
        int mid=(l+r)>>1;
        int i,j=l,k=mid+1;
        for(i=l;i<=r;i++) {
            if(t[i]<=mid) tmp[j++]=t[i];
            else tmp[k++]=t[i];
        }
        for(i=l;i<=r;i++) t[i]=tmp[i];
        solve(l,mid);
        int top=0;
        for(i=l;i<=mid;i++) {
            while(top>1&&judge(S[top-1],S[top],t[i])) top--;
            S[++top]=t[i];
        }
        for(j=1,i=mid+1;i<=r;i++) {
            while(j<top&&Y(t[i],S[j+1])>=Y(t[i],S[j])) j++;
            f[t[i]]=max(f[t[i]],Y(t[i],S[j])*A[t[i]]);
        }
        solve(mid+1,r);
        i=j=l,k=mid+1;
        while(j<=mid&&k<=r) {
            if(K[t[j]]<=K[t[k]]) tmp[i++]=t[j++];
            else tmp[i++]=t[k++];
        }
        while(j<=mid) tmp[i++]=t[j++];
        while(k<=r) tmp[i++]=t[k++];
        for(i=l;i<=r;i++) t[i]=tmp[i];
    }
    int main() {
        scanf("%d%lf",&n,&f[0]);
        int i,j;
        for(i=1;i<=n;i++) {
            scanf("%lf%lf%lf",&A[i],&B[i],&rate[i]);
            t[i]=i; pos[i]=B[i]/A[i];
        }
        sort(t+1,t+n+1,cmp);
        solve(1,n);
        printf("%.3f
    ",f[n]);
    }
    
  • 相关阅读:
    LR杂记-nmon+analyser监控linux系统资源
    accept函数
    android performClick使用
    #line 的作用是改变当前行数和文件名称
    C++常用排序法、随机数
    C语言运算符优先级及结合性
    如何高效把一字节的位对换, bit0和bit7,bit1和bit6,以此类推.
    NumPy
    Rational Rose、PowerDesign、Visio的一些比较
    vld,Bounds Checker,memwatch,mtrace,valgrind,debug_new几种内存泄露检测工具的比较,Valgrind Cheatsheet
  • 原文地址:https://www.cnblogs.com/suika/p/9202301.html
Copyright © 2011-2022 走看看