NOI2007 货币兑换
【问题描述】
小 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的变化分别为:
| 时间 | Ak | Bk | Ratek |
| 第一天 | 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行三个实数AK、BK、RateK,意义如题目中所述。
【输出格式】
只有一个实数MaxProfit,表示第N天的操作结束时能够获得的最大的金钱数目。答案保留3位小数。
-----------------------------------------------------------
正解=动归+平衡树
考虑简单Dp
状态:设f[i]为第i天能赚的最多钱为多少
方程:f[i]=max(f[i-1],na*A+nb*B)(j< i)
(na,nb为第j天能换到的A劵数量和B劵数量)
考虑优化:
设 na 为 x,nb 为 y
则sum=x*A+y*B 既 y = -A/B*x+sum/B
由于A,B为定值
sum的Max以 -A/B 为斜率的过点(x,y)的截距的Max*B
显然可能得到Max解截距的点必然是个上凸壳
建立以x为关键字的平衡树维护值(其实很简(dan)单(teng))
由于在树上的点映射的截距具有单调性,
找最大值是便可通过平衡树的前驱后继判断Max在左子树或右子树;
代码如下:
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<string>
#include<iostream>
#include<queue>
#define INF 99999999
#define LL long long
#define Cint(o) const int o=0
#define Cdou(o) const double o=0
#define Min(num1,num2) if(num1>num2) num1=num2
#define Max(num1,num2) if(num1<num2) num1=num2
struct Tree{
int l,r,f;
double x,y;
Tree(Cint(a1),Cint(a2),Cint(a3),Cdou(a4),Cdou(a5)):
l(a1),r(a2),f(a3),x(a4),y(a5){}
}a[];
int Root,Total,n;
const long double O=1e-;
void cal(double sum,double A,double B,double K,double &na,double &nb){
nb=sum/(A*K+B);
na=nb*K;
}
void rig(int now){
int f=a[now].f;
a[now].f=a[f].f;
if(a[a[f].f].l==f) a[a[f].f].l=now;
if(a[a[f].f].r==f) a[a[f].f].r=now;
a[f].f=now;
a[f].l=a[now].r;
a[a[now].r].f=f;
a[now].r=f;
} void lef(int now){
int f=a[now].f;
a[now].f=a[f].f;
if(a[a[f].f].l==f) a[a[f].f].l=now;
if(a[a[f].f].r==f) a[a[f].f].r=now;
a[f].f=now;
a[f].r=a[now].l;
a[a[now].l].f=f;
a[now].l=f;
}
double cross(Tree A,Tree B,Tree C){
return (B.x-A.x)*(C.y-A.y)-(C.x-A.x)*(B.y-A.y);
// x1 *y2-x2*y1
}
void splay(int now,int F=){
while(a[now].f!=F){
int f=a[now].f,ff=a[f].f;
if(ff==F)
if(a[f].l==now) rig(now);
else lef(now);
else
if(a[ff].l==f)
if(a[f].l==now) rig(f),rig(now);
else lef(now),rig(now);
else
if(a[f].l==now) rig(now),lef(now);
else lef(f),lef(now);
}
if(!F) Root=now;
} void creat(){
Total=;
Root=;
a[].r=;
a[].f=;
a[].x=-INF;
a[].x=INF;
}
int prev(int now){
splay(now);
now=a[now].l;
while(a[now].r) now=a[now].r;
return now;
}
int succ(int now){
splay(now);
now=a[now].r;
while(a[now].l) now=a[now].l;
return now;
}
void del(int start,int now,int end){
splay(start);
splay(end,start);
a[a[Root].r].l=;
//printf("Delte(%d)",now);
}
void maintain(int now){
int p=prev(now),s=succ(now);
if(p!=&&s!=)
if(cross(a[p],a[s],a[now])<O){
del(p,now,s);
return ;
}
while(){
if(p==) break;
int pp=prev(p);
if(pp!=&&cross(a[pp],a[now],a[p])<O)
del(pp,p,now),
p=pp;
else
break;
}
while(){
if(s==) break;
int ss=succ(s);
if(ss!=&&cross(a[now],a[ss],a[s])<O)
del(now,s,ss),
s=ss;
else
break;
}
}
void insert(double x,double y){
for(int now=Root;;){
if(a[now].x-x<O&&x-a[now].x<O){
Max(a[now].y,y);
maintain(now);
return ;
}
if(a[now].x-x>O)
if(a[now].l)
now=a[now].l;
else {
a[now].l=++Total;
a[Total].f=now;
a[Total].x=x;
a[Total].y=y;
maintain(Total);
return ;
}
else
if(a[now].r)
now=a[now].r;
else{
a[now].r=++Total;
a[Total].f=now;
a[Total].x=x;
a[Total].y=y;
maintain(Total);
return ; }
}
}
double fo(Tree now,double K){
return now.y-now.x*K;
}
int pre(int now){
//splay(now);
now=a[now].l;
while(a[now].r) now=a[now].r;
return now;
} int suc(int now){
// splay(now);
now=a[now].r;
while(a[now].l) now=a[now].l;
return now;
}
double slove(double K){
for(int now=Root;;){
if(now==){
now=suc(now);
continue ;
}
if(now==){
now=pre(now);
continue ;
} if(a[now].l){
int k=pre(now);
if(k!=&&fo(a[now],K)<fo(a[k],K)+O){
now=a[now].l;
continue ;
}
}
if(a[now].r){
int k=suc(now);
if(k!=&&fo(a[now],K)<fo(a[k],K)+O){
now=a[now].r;
continue ;
}
}
return fo(a[now],K); }
}
int main(){
double A,B,K,na,nb,s;
scanf("%d%lf",&n,&s);
scanf("%lf%lf%lf",&A,&B,&K);
creat();
cal(s,A,B,K,na,nb);
insert(na,nb);
for(int i=;i<n;i++){
scanf("%lf%lf%lf",&A,&B,&K);
double temp=slove(-A/B)*B;
Max(s,temp);
cal(s,A,B,K,na,nb);
insert(na,nb);
}
printf("%.3lf",s);
}
NOI2007 货币兑换的更多相关文章
- BZOJ 1492: [NOI2007]货币兑换Cash( dp + 平衡树 )
dp(i) = max(dp(i-1), x[j]*a[i]+y[j]*b[i]), 0<j<i. x, y表示某天拥有的最多钱去买金券, 金券a和金券b的数量. 然后就很明显了...平衡 ...
- bzoj1492[NOI2007]货币兑换Cash cdq分治+斜率优化dp
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5541 Solved: 2228[Submit][Sta ...
- cdq分治(hdu 5618 Jam's problem again[陌上花开]、CQOI 2011 动态逆序对、hdu 4742 Pinball Game、hdu 4456 Crowd、[HEOI2016/TJOI2016]序列、[NOI2007]货币兑换 )
hdu 5618 Jam's problem again #include <bits/stdc++.h> #define MAXN 100010 using namespace std; ...
- P4027 [NOI2007]货币兑换(斜率优化dp+cdq分治)
P4027 [NOI2007]货币兑换 显然,如果某一天要买券,一定是把钱全部花掉.否则不是最优(攒着干啥) 我们设$f[j]$为第$j$天时用户手上最多有多少钱 设$w$为花完钱买到的$B$券数 $ ...
- bzoj千题计划237:bzoj1492: [NOI2007]货币兑换Cash
http://www.lydsy.com/JudgeOnline/problem.php?id=1492 dp[i] 表示 第i天卖完的最大收益 朴素的dp: 枚举从哪一天买来的在第i天卖掉,或者是不 ...
- [NOI2007]货币兑换 cdq分治,斜率优化
[NOI2007]货币兑换 LG传送门 妥妥的\(n \log n\)cdq做法. 这题用cdq分治也可以\(n \log n\)但是在洛谷上竟然比一些优秀的splay跑得慢真是见了鬼了看来还是人丑常 ...
- 洛谷 P4027 [NOI2007]货币兑换 解题报告
P4027 [NOI2007]货币兑换 题目描述 小 \(Y\) 最近在一家金券交易所工作.该金券交易所只发行交易两种金券:\(A\) 纪念券(以下简称 \(A\) 券)和 \(B\) 纪念券(以下简 ...
- [NOI2007]货币兑换 --- DP + 斜率优化(CDQ分治)
[NOI2007]货币兑换 题目描述: 小 Y 最近在一家金券交易所工作.该金券交易所只发行交易两种金券:A 纪念券(以下简称 A 券)和 B 纪念券(以下简称 B 券). 每个持有金券的顾客都有一个 ...
- [BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5838 Solved: 2345[Submit][Sta ...
- BZOJ1492: [NOI2007]货币兑换Cash 【dp + CDQ分治】
1492: [NOI2007]货币兑换Cash Time Limit: 5 Sec Memory Limit: 64 MB Submit: 5391 Solved: 2181 [Submit][S ...
随机推荐
- ECMAScript位操作符
在ECMAScript中,有少数的几个操作符可以对二进制位进行直接操作,这几个操作符本身直接对二进制进行操作,所有它们的本身是非常效率的,学习这一段有助于以后的优化以及理解. ECMAScript中采 ...
- JavaScript符串中每个单词的首字母大写化
map() + replace() function titleCase(str) { var convertToArray = str.toLowerCase().split(" &quo ...
- php 简单的验证码
注意事项: 验证的隐藏域的位置一定要在调用JS前.. 如: <input type="text" name="yzm" value="" ...
- HTML5 的绘图支持- canvas
Canvas HTML5新增了一个canvas元素,它是一张空画布,开发者需要通过JavaScript脚本进行绘制. 在canvas上绘图,经过如下3步 (1) 获取canvas元素对应的DOM对象. ...
- sql join 用法
SQL JOIN 的用法 关于sql语句中的连接(join)关键字,是较为常用而又不太容易理解的关键字,下面这个例子给出了一个简单的解释 --建表table1,table2:create tabl ...
- KeepAlive详解
KeepAlive既熟悉又陌生,踩过坑的同学都知道痛.一线运维工程师踩坑之后对于KeepAlive的总结,你不应该错过! 最近工作中遇到一个问题,想把它记录下来,场景是这样的: 从上图可以看出,用户通 ...
- NSString NSMutableString copy mutableCopy retain weak strong整合
copy retain assign的差别在于对象属性的set方法 NSString 与 NSMutableString NSString是不可变字符串对象,这句话的意思,结合代码: #import ...
- 基于u-boot源码的简单shell软件实现
一.概述 1.shell概念 Shell(命令解析器),它用于接收用户输入的命令,进行解析,然后调用相应的应用程序,为使用者提供了使用软件的界面. shell是操作系统最外面的一层.shell管理你与 ...
- 实现js的类似alert效果的函数
这个简单的类似alert的函数,效果还行,至于css样式,那没的说了,笔者确实尽力了,如果读者觉得太烂,你可以随便改函数的样式的,反正,笔者觉得还可以,呵呵. <!DOCTYPE html PU ...
- cocos2dx3.4 导出节点树到XML文件
l利用cocostudio做UI和场景时,经常要去获取某个节点,cocostudio2.1开始加入了文件的概念,可以创建场景,节点,层等文件,把公用的东西创建到文件里,然后把这个文件拖到场景里使用,达 ...