1492: [NOI2007]货币兑换Cash
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 3946 Solved: 1649
[ Submit][ Status][ Discuss]
Description
Input
Output
只有一个实数MaxProfit,表示第N天的操作结束时能够获得的最大的金钱数目。答案保留3位小数。
Sample Input
1 1 1
1 2 2
2 2 3
Sample Output
HINT
Source
考虑对方案进行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;
}