E - 货币兑换Cash HYSBZ - 1492
\(N,S\),分别表示天数以及初始时拥有的钱数
接下来N行,每行三个实数\(A_i,B_i,Rate_i\),表示第\(i\)天A券和B券的汇率,和购买时要按\(a:b=rate\)的数量比购买
每天都可以随时买入和卖出,买入规则如上,卖出时A,B按同一百分比卖出,求最后一天最多能获得多少钱
\(0<AK≤10;0<BK≤10;0<RateK≤100;ans≤10^9\)(实数读入)
【提示】
1.输入文件可能很大,请采用快速的读入方式。
2.必然存在一种最优的买卖方案满足:
每次买进操作使用完所有的人民币;
每次卖出操作卖出所有的金券。
题解
注意提示, 买光或买光
- 每天手里的券可以兑换成钱表示(唯一表示), 因为当天买之前有这么多钱, 买光只有一种买法
- 每天手里的钱可以用钱表示
所以用\(f[i]\)表示当天最大的钱/券换算成的钱
- 继承pre的价值 : \(f[i] = max(f[pre])\)
- pre买,i卖 : \(f[i] = max(f[pre] / (rate[pre] * a[pre] + b[pre]) * rate[pre] * a[i] + f[pre] / (rate[pre] * a[pre] + b[pre]) * b[i]);\)
则
\(x = f[pre] / (rate[pre] * a[pre] + b[pre]) * rate[pre];\\\)
\(y = f[pre] / (rate[pre] * a[pre] + b[pre]);\\\)
\(y = -\frac{a[i]}{b[i]}x + \frac{f[i]}{b[i]}\)
其中\((x_{pre},y_{pre})\)都只含有关于\(pre\)的信息
所以相当于拿一条斜率为\(-\frac{a[i]}{b[i]}\)去切\((x_{pre},y_{pre})\),得到的截距越大, \(f[i]\)越大
注意到如果以\(j\)继承比\(k\)更优,则\((x_j,y_j)与(x_k,y_k)\)的斜率要\(>-\frac{a[i]}{b[i]}\)(画图)
所以当\((mid+1,r)\)斜率递减时,可以从左到右依次在\((l,mid)\)组成的上凸线上找到优的继承点
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 10;
const double EPS = 1e-9;
inline int in()
{
int x = 0, flag = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') flag = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return x * flag;
}
int n, s, tot;
double f[MAXN];
struct Node
{
double a, b, rate, k, x, y;
int id ;
} q[MAXN], p[MAXN], stk[MAXN];
bool cmpxy(Node A, Node B) { return (A.x == B.x) ? (A.y < B.y) : (A.x < B.x); }
bool cmpk(Node A, Node B) { return A.k > B.k; }
double getk(Node A, Node B)
{
if (fabs(A.x - B.x) < EPS) return 1e20;
return (A.y - B.y) / (A.x - B.x);
}
void solve(int l, int r)
{
if (l == r)
{
f[l] = max(f[l - 1], f[l]);
p[l].y = f[l] / (p[l].rate * p[l].a + p[l].b);
p[l].x = p[l].y * p[l].rate;
return ;
}
int mid = (l + r) >> 1;
solve(l, mid);
for (int i = l; i <= r; i ++) q[i] = p[i];
sort(q + l, q + mid + 1, cmpxy);
sort(q + mid + 1, q + r + 1, cmpk);
int top = 0;
for (int i = l; i <= mid; i ++)
{
while (top > 1 && getk(stk[top - 1], stk[top]) < getk(stk[top], q[i]) + EPS) top --;
stk[++top] = q[i];
}
for (int i = mid + 1, j = 1; i <= r; i ++)
{
while (j + 1 <= top && q[i].k < getk(stk[j], stk[j + 1]) + EPS) j ++;
f[q[i].id] = max(f[q[i].id], stk[j].x * q[i].a + stk[j].y * q[i].b);
}
solve(mid + 1, r);
}
int main()
{
n = in(), s = in();
f[0] = s;
for (int i = 1; i <= n; i ++)
{
double a, b, rate;
scanf("%lf%lf%lf", &a, &b, &rate);
p[i] = (Node) { a, b, rate, - a / b, 0, 0, i };
}
solve(1, n);
printf("%.3lf\n", f[n]);
return 0;
}
/*
*/