1492: [NOI2007]货币兑换Cash

12 篇文章 0 订阅
8 篇文章 0 订阅

1492: [NOI2007]货币兑换Cash

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 3946   Solved: 1649
[ Submit][ Status][ Discuss]

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

Source

[ Submit][ Status][ Discuss]



考虑对方案进行dp

令fi:第i天能拥有的最大RMB数量

首先,显然每日的操作只能是全部买进或卖出,或者不操作

然后AB券的数量能用来衡量金钱

xi:第i天能拥有的最大A券数量,xi = fi/(BiAi + Bi)*Ri

yi:第i天能拥有的最大B券数量,yi = fi/(BiAi + Bi)

那么,fi = max{xk*Ai + yk*Bi} (k < i) 或者 fi = fi-1,也就是这天不操作

对于fi = xk*Ai + yk*Bi,移向整理得,yk = -Ai/Bi*xk + fi/Bi

如果把(xk,yk)看作二维平面上的点,那么上式就等价于一条过该点的直线

想要fi取得最大值,那么该直线在y轴上的截距最大就行了

也就是说,前面的信息全部转换成二维平面上的点,从中找到一个最优点

显然最优点在凸包上,然后选择点造成的影响是个单峰函数

维护动态凸包然后二分??参考ppt

这个ppt讲的cdq分治还是很不错的,,总结一下做法

每次询问的斜率是固定的,对于询问区间[l,r],假设我们已经求好[l,mid]的凸包,[mid+1,r]的询问,按照斜率递减排好序,那么凸包上的点用来更新询问,显然具有单调性(画画图--)

合并两个凸包又是O(n)的,类似归并排序

于是这题就可以做到O(nlogn)解决,而且代码不难写


然后有一些奇怪的地方要注意,,貌似直接用叉积判断向量关系,此题会溢出?

使用斜率的时候要判斜率取INF,,这就要善用eps了

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = 1E5 + 10;
typedef double DB;
const DB eps = 1E-9;
const DB INF = 1E9;
 
struct data{
    int day; DB k; data(){}
    data(int day,DB k): day(day),k(k){}
    bool operator < (const data &b) const {return k > b.k;}
}Q[maxn],G[maxn];
 
struct Point{
    DB x,y; Point(){}
    Point(DB x,DB y): x(x),y(y){}
    bool operator < (const Point &b) const {
        if (x < b.x) return 1;
        if (x > b.x) return 0;
        return y < b.y;  
    }
    Point operator - (const Point &b) {return Point(x - b.x,y - b.y);}
}s[maxn];
typedef Point Vector;
 
int n;
DB S,f[maxn],A[maxn],B[maxn],R[maxn];
 
DB getk(Point a,Point b) 
{
	if (fabs(b.x - a.x) <= eps) return (fabs(b.y - a.y) >= eps)?INF:-INF;
	return (b.y - a.y)/(b.x - a.x);
}
 
void Merge(vector <Point> v1,vector <Point> v2,vector <Point> &v)
{
    int tp1,tp2,tp; tp1 = tp2 = tp = 0;
    while (tp1 < v1.size() && tp2 < v2.size()) 
        if (v1[tp1] < v2[tp2]) s[++tp] = v1[tp1++];
        else s[++tp] = v2[tp2++];
    while (tp1 < v1.size()) s[++tp] = v1[tp1++];
    while (tp2 < v2.size()) s[++tp] = v2[tp2++];
    int tot = tp; tp = 0;
    for (int i = 1; i <= tot; i++) {
        while (tp > 1 && getk(s[i],s[tp]) >= getk(s[tp],s[tp-1])) --tp;
        s[++tp] = s[i];
    }
    for (int i = 1; i <= tp; i++) v.push_back(s[i]);
}
 
DB Query(Point p,data q)
{
    return p.x*A[q.day] + p.y*B[q.day];
}
 
void Solve(int l,int r,vector <Point> &v)
{
    if (l == r) {
        f[l] = max(f[l],f[l-1]);
        DB F = R[l]*A[l] + B[l];
        Point g; g = Point(f[l]*R[l]/F,f[l]/F);
        v.push_back(g); return;
    }
    int mid = (l + r) >> 1,t1 = l - 1,t2 = mid;
    for (int i = l; i <= r; i++)
        if (Q[i].day <= mid) G[++t1] = Q[i];
        else G[++t2] = Q[i];
    for (int i = l; i <= r; i++) Q[i] = G[i];
    vector <Point> v1,v2; v1.clear(); v2.clear();
    Solve(l,mid,v1); int tp = 0;
    for (int i = mid + 1; i <= r; i++) {
        DB F = Query(v1[tp],Q[i]);
        while (tp < v1.size() - 1) {
            DB ret = Query(v1[tp+1],Q[i]);
            if (ret <= F) break;
            F = ret; ++tp;
        }
        f[Q[i].day] = max(f[Q[i].day],F);
    }
    Solve(mid + 1,r,v2); Merge(v1,v2,v);
}
 
int main()
{
    freopen("1492.in","r",stdin);
    freopen("1492.out","w",stdout);
     
    cin >> n >> S;
    for (int i = 1; i <= n; i++) {
        scanf("%lf%lf%lf",&A[i],&B[i],&R[i]);
        Q[i] = data(i,-A[i]/B[i]);
    }
    sort(Q + 1,Q + n + 1);
    vector <Point> v; v.clear();
    f[1] = S; Solve(1,n,v);
    printf("%.3lf",f[n]);
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值