zoukankan      html  css  js  c++  java
  • [NOI2007]货币兑换

    题目描述

    小 Y 最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A 纪念券(以下简称 A 券)和 B 纪念券(以下简称 B 券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。

    每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 K 天中 A 券和 B 券的价值分别为 AKA_KAKBKB_KBK (元/单位金券)。

    为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。

    比例交易法分为两个方面:

    a) 卖出金券:顾客提供一个[0,100]内的实数 OP 作为卖出比例,其意义为:将 OP%的 A 券和 OP%的 B 券以当时的价值兑换为人民币;

    b) 买入金券:顾客支付 IP 元人民币,交易所将会兑换给用户总价值为IP 的金券,并且,满足提供给顾客的 A 券和 B 券的比例在第 K 天恰好为 RateKRate_KRateK

    例如,假定接下来 3 天内的 AkA_kAkBkB_kBkRateKRate_KRateK 的变化分别为:

    时间 AkA_kAk BkB_kBk RatekRate_kRatek

    第一天 1 1 1

    第二天 1 2 2

    第三天 2 2 3

    假定在第一天时,用户手中有 100 元人民币但是没有任何金券。

    用户可以执行以下的操作:

    时间 用户操作 人民币(元) A 券的数量 B 券的数量

    开户 无 100 0 0

    第一天 买入 100 元 0 50 50

    第二天 卖出 50% 75 25 25

    第二天 买入 60 元 15 55 40

    第三天 卖出 100% 205 0 0

    注意到,同一天内可以进行多次操作。

    小 Y 是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经知道了未来 N 天内的 A 券和 B 券的价值以及 Rate。他还希望能够计算出来,如果开始时拥有 S 元钱,那么 N 天后最多能够获得多少元钱。

    输入输出格式

    输入格式:

    第一行两个正整数 N、S,分别表示小 Y 能预知的天数以及初始时拥有的钱数。

    接下来 N 行,第 K 行三个实数 AKA_KAKBKB_KBKRateKRate_KRateK ,意义如题目中所述。

    输出格式:

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

    输入输出样例

    输入样例#1: 复制
    3 100
    1 1 1
    1 2 2
    2 2 3
    输出样例#1: 复制
    225.000

    说明

    时间 用户操作 人民币(元) A 券的数量 B 券的数量

    开户 无 100 0 0

    第一天 买入 100 元 0 50 50

    第二天 卖出 100% 150 0 0

    第二天 买入 150 元 0 75 37.5

    第三天 卖出 100% 225 0 0

    本题没有部分分,你的程序的输出只有和标准答案相差不超过0.0010.0010.001 时,才能获得该测试点的满分,否则不得分。

    测试数据设计使得精度误差不会超过 10−710^{-7}107 。

    对于 40%的测试数据,满足 N ≤ 10;

    对于 60%的测试数据,满足 N ≤ 1 000;

    对于 100%的测试数据,满足 N ≤ 100 000;

    对于 100%的测试数据,满足:

    0 < AKA_KAK ≤ 10;

    0 < BKB_KBK ≤ 10;

    0 < RateKRate_KRateK ≤ 100

    MaxProfit ≤ 10910^9109 ;

    输入文件可能很大,请采用快速的读入方式。

    必然存在一种最优的买卖方案满足:

    每次买进操作使用完所有的人民币;

    每次卖出操作卖出所有的金券。

    令f[i]为当前的收益(如果是金劵则兑换为现金)

    因为根据贪心身上不是现金就是金劵

    显然$O(n^2)$的DP出来了

    $f[i]=max(A_i*x_j+B_i*y_j)$

    $x_j$和$y_j$为在j处的f[j]能买的A劵和B劵数

    可以算出来

    100分要用到斜率优化+CDQ

    每一次处理出[l,mid]内部更新的f值

    然后用这些更新[mid+1,r]的f值

    然后在考虑[mid+1,r]的内部更新

    重点是用[l,mid]更新[mid+1,r]

    且要在线性时间

    将上述式子变为斜距式:

    $y=-frac{A_i}{B_i}x+frac{f_i}{B_i}$

    如果$f_i$要尽可能大,那么截距就大,斜率是固定的$rate_i$

    对于[l,mid]中的(x,y)维护一个上凸包,一个斜率为k

    显然只要使第$i$天的“这条直线”切于前面[l,mid]天的点对组成的上凸包时即可得到fimax 。

    于是我们可以把[mid+1,r]按k排序,于是可以单调栈

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 using namespace std;
     7 struct ZYYS
     8 {
     9   int id;
    10   double k,x,y;
    11 }s[100001];
    12 bool cmp1(ZYYS a,ZYYS b)
    13 {
    14   return a.k>b.k;
    15 }
    16 bool cmp2(ZYYS a,ZYYS b)
    17 {
    18   return a.id<b.id;
    19 }
    20 bool cmp3(ZYYS a,ZYYS b)
    21 {
    22   return (a.x==b.x)?a.y<b.y:a.x<b.x;
    23 }
    24 double f[100001],rate[100001],a[100001],b[100001],inf=-1e15,eps=1e-10;
    25 int n;
    26 double S,ans;
    27 double get_k(int j,int i)
    28 {
    29   if (fabs(s[i].x-s[j].x)<=eps) return -inf;
    30   return (s[i].y-s[j].y)/(s[i].x-s[j].x);
    31 }
    32 void solve(int l,int r)
    33 {int i;
    34   if (l==r)
    35     {
    36       f[l]=max(f[l-1],f[l]);
    37       s[l].x=(rate[l]*f[l])/(rate[l]*a[l]+b[l]);
    38       s[l].y=(f[l])/(rate[l]*a[l]+b[l]);
    39       return;
    40     }
    41   int mid=(l+r)/2;
    42   sort(s+l,s+r+1,cmp2);
    43   solve(l,mid);
    44   int top=0,st[100001];
    45   for (i=l;i<=mid;i++)
    46     {
    47       while (top>1&&get_k(st[top-1],st[top])<get_k(st[top],i)) top--;
    48       st[++top]=i;
    49     }
    50   sort(s+mid+1,s+r+1,cmp1);
    51   int loc=1;
    52   for (i=mid+1;i<=r;i++)
    53     {
    54       while (loc<top&&s[i].k<get_k(st[loc],st[loc+1])) loc++;
    55       f[s[i].id]=max(f[s[i].id],a[s[i].id]*s[st[loc]].x+b[s[i].id]*s[st[loc]].y);
    56     }
    57   solve(mid+1,r);
    58   sort(s+l,s+r+1,cmp3);
    59 }
    60 int main()
    61 {int i;
    62   cin>>n>>S;
    63   f[0]=S;
    64   for (i=1;i<=n;i++)
    65     {
    66       scanf("%lf%lf%lf",&a[i],&b[i],&rate[i]);
    67       s[i].id=i;s[i].k=-a[i]/b[i];
    68     }
    69   solve(1,n);
    70   for (i=0;i<=n;i++)
    71     ans=max(ans,f[i]);
    72   printf("%.3lf
    ",ans);
    73 }
  • 相关阅读:
    JavaScript 的定时(Timing )事件——setTimeout()与setInterval()
    HTML5+CSS3制作无限滚动与文字跳动效果
    动画属性与过渡属性与渐变属性(全)
    JavaScript 数组2—关联数组
    JavaScript 数组1—索引数组
    什么是JavaScript循环结构?
    JavaScript分支结构Ⅱ—switch-case
    JavaScript分支结构Ⅰ—IF-ELSE
    JavaScript 正则表达式——预定义类,边界,量词,贪婪模式,非贪婪模式,分组,前瞻
    SEO搜索引擎优化是什么?
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/8627235.html
Copyright © 2011-2022 走看看