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 ...
随机推荐
- greenplum学习
公司TM蛋疼,动不动让你学习新东西,就是不让你闲下来,本着胳膊拧不过大腿定律,忍了,这是背景. 好吧哥端起一本厚厚的<GreenPlum企业应用实战>,打开百度开始GP的学习之路: GP只 ...
- 常用js函数
1.获得元素到页面的绝对距离 function getPos(obj) { var pos = {left:0, top:0}; while (obj) { pos.left += obj.offse ...
- init.d文件夹
/etc/init.d 是 /etc/rc.d/init.d 的软链接(soft link). [root@asus ~]# ll /etc/init.d lrwxrwxrwx. 1 root roo ...
- php 遍历一个文件夹下的所有文件和子文件夹
<?php function my_scandir($dir) { $files=array(); if(is_dir($dir)) { if($handle=opendir($dir)) { ...
- Windows下MySQL数据库备份脚本(一)
说明: MySQL数据库安装目录:C:\Program Files\MySQL\MySQL Server 5.0 MySQL数据库存放目录:C:\Program Files\MySQL\MySQL S ...
- 得到bundle seed id
- (NSString *)bundleSeedID { NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys: (__br ...
- Java RMI简单例子HelloWorld
Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ...
- AD10 gerber生成,及导入cam350 多图详细步骤
Protel99转Gerber文件导入到CAM350中看为什么钻孔层偏位 这是因为你导入CAM350 时的格式没有设置正确.你用PROTEL 导出钻孔 TXT 时记住是什么格式,例如: 2:3,2:4 ...
- MVVM_Android-CleanArchitecture
前言 "Architecture is About Intent, not Frameworks" - Robert C. Martin (Uncle Bob) Uncle Bob ...
- php 返回json 解析 报Wide character in print
php 返回json: zabbix:/var/www/html/DEVOPS/Home/Lib/Action# vim EquipmentAction.class.php <?php head ...