P4027 [NOI2007] 货币兑换
P4027 [NOI2007] 货币兑换
题目描述
小 Y 最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A 纪念券(以下简称 A 券)和 B 纪念券(以下简称 B 券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。
每天随着市场的起伏波动,两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 K K K 天中 A 券和 B 券的价值分别为 A K A_K AK 和 B K B_K BK(元/单位金券)。
为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法。
比例交易法分为两个方面:
a) 卖出金券:顾客提供一个 [ 0 , 100 ] [0, 100] [0,100] 内的实数 O P OP OP 作为卖出比例,其意义为:将 O P % OP\% OP% 的 A 券和 O P % OP\% OP% 的 B 券以当时的价值兑换为人民币;
b) 买入金券:顾客支付 I P IP IP 元人民币,交易所将会兑换给用户总价值为 I P IP IP 的金券,并且,满足提供给顾客的 A 券和 B 券的比例在第 K K K 天恰好为 R a t e K \mathrm{Rate}_ K RateK;
例如,假定接下来 3 3 3 天内的 A K , B K , R a t e K A_K,B_K,\mathrm{Rate}_ K AK,BK,RateK 的变化分别为:
时间 | A K A_K AK | B K B_K BK | R a t e K \mathrm{Rate}_ K RateK |
---|---|---|---|
第一天 | 1 1 1 | 1 1 1 | 1 1 1 |
第二天 | 1 1 1 | 2 2 2 | 2 2 2 |
第三天 | 2 2 2 | 2 2 2 | 3 3 3 |
假定在第一天时,用户手中有 100 100 100 元人民币但是没有任何金券。
用户可以执行以下的操作:
时间 | 用户操作 | 人民币(元) | A 券的数量 | B 券的数量 |
---|---|---|---|---|
开户 | 无 | 100 100 100 | 0 0 0 | 0 0 0 |
第一天 | 买入 100 100 100 元 | 0 0 0 | 50 50 50 | 50 50 50 |
第二天 | 卖出 50 % 50\% 50% | 75 75 75 | 25 25 25 | 25 25 25 |
第二天 | 买入 60 60 60 元 | 15 15 15 | 55 55 55 | 40 40 40 |
第三天 | 卖出 100 % 100\% 100% | 205 205 205 | 0 0 0 | 0 0 0 |
注意到,同一天内可以进行多次操作。
小 Y 是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经知道了未来 N N N 天内的 A 券和 B 券的价值以及 R a t e \mathrm{Rate} Rate。他还希望能够计算出来,如果开始时拥有 S S S 元钱,那么 N N N 天后最多能够获得多少元钱。
输入格式
第一行两个正整数 N , S N,S N,S,分别表示小 Y 能预知的天数以及初始时拥有的钱数。
接下来 N N N 行,第 K K K 行三个实数 A K , B K , R a t e K A_K,B_K,\mathrm{Rate} _ K AK,BK,RateK ,意义如题目中所述。
输出格式
只有一个实数 M a x P r o f i t \mathrm{MaxProfit} MaxProfit,表示第 N N N 天的操作结束时能够获得的最大的金钱数目。答案保留 3 3 3 位小数。
样例
样例输入
3 100
1 1 1
1 2 2
2 2 3
样例输出
225.000
提示
时间 | 用户操作 | 人民币(元) | A 券的数量 | B 券的数量 |
---|---|---|---|---|
开户 | 无 | 100 100 100 | 0 0 0 | 0 0 0 |
第一天 | 买入 100 100 100 元 | 0 0 0 | 50 50 50 | 50 50 50 |
第二天 | 卖出 100 % 100\% 100% | 150 150 150 | 0 0 0 | 0 0 0 |
第二天 | 买入 150 150 150 元 | 0 0 0 | 75 75 75 | 37.5 37.5 37.5 |
第三天 | 卖出 100 % 100\% 100% | 225 225 225 | 0 0 0 | 0 0 0 |
本题没有部分分,你的程序的输出只有和标准答案相差不超过 0.001 0.001 0.001 时,才能获得该测试点的满分,否则不得分。
测试数据设计使得精度误差不会超过 1 0 − 7 10^{-7} 10−7 。
对于 40 % 40\% 40% 的测试数据,满足 N ≤ 10 N \le 10 N≤10。
对于 60 % 60\% 60% 的测试数据,满足 N ≤ 1000 N \le 1 000 N≤1000。
对于 100 % 100\% 100% 的测试数据,满足 N ≤ 1 0 5 N \le 10^5 N≤105。
对于 100 % 100\% 100% 的测试数据,满足:
0 < A K ≤ 10 0 < A_K \leq 10 0<AK≤10, 0 < B K ≤ 10 0 < B_K\le 10 0<BK≤10, 0 < R a t e K ≤ 100 0 < \mathrm{Rate}_K \le 100 0<RateK≤100, M a x P r o f i t ≤ 1 0 9 \mathrm{MaxProfit} \leq 10^9 MaxProfit≤109。
输入文件可能很大,请采用快速的读入方式。
必然存在一种最优的买卖方案满足:
每次买进操作使用完所有的人民币,每次卖出操作卖出所有的金券。
题解
前置知识: c d q cdq cdq分治
题目已经说了,必然存在一种最优的买卖方案满足:每次买进操作使用完所有的人民币,每次卖出操作卖出所有的金券。所以我们设 f i f_i fi表示在第 i i i天将所有金券卖出所获得的最多的钱。设第 i i i天得到的金券数是第 j j j天买入的, x j x_j xj表示第 j j j天A券的数量, y j y_j yj表示第 j j j天B券的数量,则有:
x j = r j f j r j a j + b j , y j = f j r j a j + b j x_j=\frac{r_j f_j}{r_j a_j+b_j},y_j=\frac{f_j}{r_ja_j+b_j} xj=rjaj+bjrjfj,yj=rjaj+bjfj
即可得到状态转移方程 f i = x j a i + y j b i f_i=x_ja_i+y_jb_i fi=xjai+yjbi,变为斜截式方程得 y j = − a i b i x j + f i b i y_j=-\frac{a_i }{b_i}x_j+\frac{f_i}{b_i} yj=−biaixj+bifi
此时,对于平面上每个点 ( x , y ) (x,y) (x,y),我们只需用斜率为 − a i b i -\frac{a_i}{b_i} −biai的直线去切其中一个点,可以使截距最大的点即为最优决策点。
至于如何求使其截距最大的点,斜率优化DP中有讲解(可看可不看,下文有讲解)
接下来就用 c d q cdq cdq分治来解决问题。对于当前区间 [ l , r ] [l,r] [l,r],将其分为左右两个区间。对左区间按 x i x_i xi从小到大排序,对右区间按 − a i b i -\frac{a_i}{b_i} −biai从小到大排序。用栈来维护左区间所形成的斜率单调递减的凸包,然后用右区间的点依次匹配左区间的点。对于每次匹配,如果左区间的点 i i i与左边的点连成的线斜率小于当前当前枚举到的斜率,则 i i i左边的点比 i i i更优。
因为用的是栈,所以平摊下来每次是 O ( n ) O(n) O(n)的,总时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
code
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-8,inf=1e9;
int n,h[100005];
double f[100005];
struct node{double a,b,r,x,y,k;int id;
}w[100005],w1[100005];
bool cmp1(node ax,node bx){return ax.k<bx.k;
}
bool cmp2(node ax,node bx){return ax.x<bx.x;
}
double gt(int i,int j){if(fabs(w[i].x-w[j].x)<=eps) return inf;return (w[j].y-w[i].y)/(w[j].x-w[i].x);
}
void cdq(int l,int r){if(l==r){f[l]=max(f[l],f[l-1]);w[l].y=f[l]/(w[l].a*w[l].r+w[l].b);w[l].x=w[l].y*w[l].r;return;}int mid=l+r>>1;for(int o=l,i=l-1,j=mid;o<=r;o++){if(w[o].id<=mid) w1[++i]=w[o];else w1[++j]=w[o];}for(int o=l;o<=r;o++) w[o]=w1[o];cdq(l,mid);int tp=0;for(int i=l;i<=mid;i++){while(tp>=2&>(h[tp],i)+eps>gt(h[tp-1],h[tp])) --tp;h[++tp]=i;}for(int i=mid+1;i<=r;i++){while(tp>=2&>(h[tp-1],h[tp])<=w[i].k+eps) --tp;f[w[i].id]=max(f[w[i].id],w[h[tp]].x*w[i].a+w[h[tp]].y*w[i].b);}cdq(mid+1,r);sort(w+l,w+r+1,cmp2);
}
int main()
{scanf("%d%lf",&n,&f[0]);for(int i=1;i<=n;i++){scanf("%lf%lf%lf",&w[i].a,&w[i].b,&w[i].r);w[i].k=-w[i].a/w[i].b;w[i].id=i;}sort(w+1,w+n+1,cmp1);cdq(1,n);printf("%.3f",f[n]);return 0;
}
P4027 [NOI2007] 货币兑换相关推荐
- bzoj千题计划237:bzoj1492: [NOI2007]货币兑换Cash
http://www.lydsy.com/JudgeOnline/problem.php?id=1492 dp[i] 表示 第i天卖完的最大收益 朴素的dp: 枚举从哪一天买来的在第i天卖掉,或者是不 ...
- 【BZOJ1492】[NOI2007]货币兑换Cash 斜率优化+cdq分治
[BZOJ10492][NOI2007]货币兑换Cash Description 小Y最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下简称B券).每 ...
- 【bzoj 1492】【codevs 1797】 [NOI2007]货币兑换Cash (dp+cdq分治)
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MB Submit: 3803 Solved: 1604 [Submit][S ...
- [BZOJ1492] [NOI2007]货币兑换Cash 斜率优化+cdq/平衡树维护凸包
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MB Submit: 5907 Solved: 2377 [Submit][S ...
- BZOJ 1492: [NOI2007]货币兑换Cash [CDQ分治 斜率优化DP]
传送门 题意:不想写... 扔链接就跑 好吧我回来了 首先发现每次兑换一定是全部兑换,因为你兑换说明有利可图,是为了后面的某一天两种卷的汇率差别明显而兑换 那么一定拿全利啊,一定比多天的组合好 $f[ ...
- NOI2007 货币兑换 - CDQ分治斜率优化dp
斜率优化dp维护一个凸壳.如果\(x, y\)坐标都递增,可以用单调队列,如果只有\(x\)递增,可以在凸壳上二分斜率,如果\(x, y\)都不递增,则需要在凸包中插入,可以用平衡树或cdq分治维护. ...
- bzoj1492: [NOI2007]货币兑换Cash
码了我两个星期的题啊,终于写完了,感觉一半的时间都在调splay,后面写cdq好像轻松一点,cdq码量小一倍,而且又好想(可能是我调的时候理解了)感觉这种黑科技很nb,关键是可以减少细节出错(像我这种 ...
- [NOI2007]货币兑换Cash(DP+动态凸包)
第一次打动态凸包维护dp,感觉学到了超级多的东西. 首先,set是如此的好用!!!可以通过控制一个flag来实现两种查询,维护凸包和查找斜率k 不过就是重载运算符和一些细节方面有些恶心,90行解决 后 ...
- [NOI2007] 货币兑换 解题报告
https://www.zybuluo.com/rebirth1120/note/1528243 转载于:https://www.cnblogs.com/BruceW/p/11315368.html
最新文章
- android兼容小米xiaomi刘海屏解决方案
- python 快速排序_小白入门知识详解:Python实现快速排序的方法(含实例代码)...
- myeclipse10.7安装git插件
- 集合2--毕向东java基础教程视频学习笔记
- Elasticsearch集群和索引常用命令
- Linux下串口通信详解
- 【C语言进阶深度学习记录】三十一 数组作为函数参数时退化为指针
- 用c语言程序算自己的年龄,新手编的小程序:计算年龄和出生后经历的天数的小程序...
- java计算时间差距_硬计算和软计算之间的差异
- Hibernate 原汁原味的四种抓取策略(转)
- Windows按键翻译
- 信号检测与估计c语言,清华大学出版社-图书详情-《信号检测与估计(第2版)》...
- Java五子棋书,本文实例讲述了java实现的五子棋游戏代码,分享给大家供大家参考,具体代码如下package gyb.exam; import java.awt.Bo...
- MBR15200FAC-ASEMI插件肖特基二极管MBR15200FAC
- MATLAB等额还款代码,matlab算法实现对等额本息和等额本金两种还款方式的计算
- 机房综合布线施工主要是几个方面
- 2023 年大淘宝 Web 端技术概览
- 关于网络游戏的影响(腾讯游戏)
- flutter 图片加载
- python手写数字识别实验报告_Python代码实现简单的MNIST手写数字识别(适合初学者看)...