P4027 [NOI2007]货币兑换

算法:dp+斜率优化

题面十分冗长,题意大概是有一种金券每天价值会有变化,你可以在某些时间点买入或卖出所有的金券,问最大收益

根据题意,很容易列出朴素的状态转移方程:

设\(f_i\)为第\(i\)天B券的数量,\(ans_j\)为以当前价格卖光第\(j\)天的金券可获得的收益,则

\(f_i=\max{ans_j}/(a_i*r_i+b_i)\)

\(O(n)\)求\(\max{ans_j}\),复杂度为\(O(n^2)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN=1024*100;
int N;
double S,a[MAXN],b[MAXN],r[MAXN],f[MAXN],ans;
int main(){
scanf("%d%lf",&N,&S);
for(int i=1;i<=N;i++){
scanf("%lf%lf%lf",&a[i],&b[i],&r[i]);
}
ans=S;
f[1]=ans*r[1]/(a[1]*r[1]+b[1]);
for(int i=1;i<=N;i++){
for(int j=1;j<i;j++){
ans=max(ans,f[j]*a[i]+f[j]/r[j]*b[i]);
}
f[i]=ans*r[i]/(a[i]*r[i]+b[i]);
}
printf("%.3lf",ans);
return 0;
}

然而此题要求\(O(nlogn)\)的做法,故朴素的dp无法AC,此时可以想到斜率优化

step1:转化方程

设\(a_i\)为第\(i\)天A券的价格,\(b_i\)为第\(i\)天B券的价格,\(ca_i\)为第\(i\)天A券的数量,\(cb_i\)为第\(i\)天B券的数量,\(f_i\)为第\(i\)天的最大收益

则\(f_i=\max{ca_j*a_i+cb_j*b_i}\)

∴如果j比k更优,有

\(ca_j*a_i+cb_j*b_i>ca_k*a_i+cb_k*b_i\)

∴\((cb_j-cb_k)*b_i>-a_i*(ca_j-ca_k)\)

∴\(\frac{cb_j-cb_k}{ca_j-ca_k}>-\frac{a_i}{b_i}\)

以\(ca\)为横坐标\(cb\)为纵坐标建立如图所示平面直角坐标系

剩下的就可以-斜率优化-

不过此题不比模板题,ca与cb不满足单调性,所以需要用平衡树或cdq等方法维护,此处用stl_set维护(因为是凸壳,所以斜率与横坐标同时满足单调性,可以用一个关键字查找)

此处切线只要找\(-\frac{a}{b}\)的lower_bound即可(代码中的query)

此处插入点要把当前位置两边的点都判断一下是否与上凸壳冲突,删除(代码中的insert)

实现就不难了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1024*100;
int N;
double S,A[MAXN],B[MAXN],R[MAXN],F[MAXN],CA[MAXN],CB[MAXN];
inline double ABS(double x) { return x>0?x:-x; }
struct node {
double X,Y,K;
int flag;
node() { X=Y=K=flag=0; }
node(double x,double y) { X=x; Y=y; flag=0; }
friend inline bool operator<(node x,node y) {
if(x.flag||y.flag) { return x.K>y.K; }
return x.X<y.X;
}
friend inline bool operator==(node x,node y) { return ABS(x.X-y.X)<1e-8; }
friend inline double operator*(node x,node y) { return (y.Y-x.Y)/(y.X-x.X); }//斜率
inline bool error() { return X<-1e20||Y<-1e20; }
} error(-1e21,-1e21);
set<node> dq;
typedef set<node>::iterator ITER;
inline node next(node x) {
ITER ii=dq.upper_bound(x);
return ii==dq.end()?error:*ii;
}
inline node lower(node x) {
ITER ii=dq.lower_bound(x);
return ii==dq.end()?error:*ii;
}
inline node pre(node x) {
ITER ii=dq.lower_bound(x);
return ii==dq.begin()?error:*(--ii);
}
inline void insert(node x) {
if(dq.empty()) {
x.K=0;
dq.insert(x);
return;
}
node L=pre(x),R=lower(x);
if((L.error()&&x.Y<R.Y)||(!L.error()&&!R.error()&&L*x-L*R<1e-8)/**/||(x==R)) { return; }
R=next(x);
while(1) {
L=R;
R=next(L);
if(L.error()||R.error()||(x*L)-(L*R)>=1e-8) { break; }
dq.erase(L);
}
L=pre(x);
while(1) {
R=L;
L=pre(R);
if(L.error()||R.error()||(L*R)-(R*x)>=1e-8) { break; }
dq.erase(R);
}
L=pre(x);
R=next(x);
x.K=(L.error()?0:(L*x));
dq.insert(x);
if(!R.error()) {
dq.erase(R);
R.K=(x*R);
dq.insert(R);
}
}
inline double query(double x,double y) {
node ii;
ii.flag=1;
ii.K=-x/y;
ii=*(--dq.lower_bound(ii));
return ii.error()?0:ii.X*x+ii.Y*y;
}
int main() {
scanf("%d%lf",&N,&S);
for(int i=1; i<=N; i++) {
scanf("%lf%lf%lf",&A[i],&B[i],&R[i]);
}
F[1]=S;
CB[1]=S/(A[1]*R[1]+B[1]);
CA[1]=CB[1]*R[1];
insert(node(CA[1],CB[1]));
for(int i=2; i<=N; i++) {
F[i]=max(F[i-1],query(A[i],B[i]));
CB[i]=F[i]/(A[i]*R[i]+B[i]);
CA[i]=CB[i]*R[i];
insert(node(CA[i],CB[i]));
}
printf("%.3lf",F[N]);
return 0;
}

洛谷P4027 [NOI2007]货币兑换的更多相关文章

  1. 洛谷 P4027 [NOI2007]货币兑换 解题报告

    P4027 [NOI2007]货币兑换 题目描述 小 \(Y\) 最近在一家金券交易所工作.该金券交易所只发行交易两种金券:\(A\) 纪念券(以下简称 \(A\) 券)和 \(B\) 纪念券(以下简 ...

  2. LOJ 2353 & 洛谷 P4027 [NOI2007]货币兑换(CDQ 分治维护斜率优化)

    题目传送门 纪念一下第一道(?)自己 yy 出来的 NOI 题. 考虑 dp,\(dp[i]\) 表示到第 \(i\) 天最多有多少钱. 那么有 \(dp[i]=\max\{\max\limits_{ ...

  3. 洛谷P4027 [NOI2007]货币兑换(dp 斜率优化 cdq 二分)

    题意 题目链接 Sol 解题的关键是看到题目里的提示... 设\(f[i]\)表示到第\(i\)天所持有软妹币的最大数量,显然答案为\(max_{i = 1}^n f[i]\) 转移为\(f_i = ...

  4. P4027 [NOI2007]货币兑换(斜率优化dp+cdq分治)

    P4027 [NOI2007]货币兑换 显然,如果某一天要买券,一定是把钱全部花掉.否则不是最优(攒着干啥) 我们设$f[j]$为第$j$天时用户手上最多有多少钱 设$w$为花完钱买到的$B$券数 $ ...

  5. 洛谷 P2047 [NOI2007]社交网络 解题报告

    P2047 [NOI2007]社交网络 题目描述 在社交网络(\(social\) \(network\))的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有\ ...

  6. 洛谷——P2047 [NOI2007]社交网络

    P2047 [NOI2007]社交网络 $Floyd$,一眼看到就是他(博主是不小心瞄到了这个题的标签吧qwq) 这个题目只要预处理出$S$到$T$的最短路的条数即可,类似$Spfa$的更新方法 如果 ...

  7. 洛谷P2047 [NOI2007]社交网络 [图论,最短路计数]

    题目传送门 社交网络 题目描述 在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有n个人,人与人之间有不同程度的关系. ...

  8. P4027 [NOI2007]货币兑换

    传送门 首先有一个显然的贪心,每次操作都要做到底,为了最优不会出现只卖一部分或者只买一部分的操作 所以设 $f[i]$ 表示前 $i$ 天得到的最大价值,那么对于每一个 $i$,枚举所有 $j< ...

  9. BZOJ1491 洛谷2047 NOI2007 社交网络

    Description: 在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有n个人,人与人之间有不同程度的关系.我 们 ...

随机推荐

  1. 利用IK分词器,自定义分词规则

    IK分词源码下载地址:https://code.google.com/p/ik-analyzer/downloads/list lucene源码下载地址:http://www.eu.apache.or ...

  2. Redis事务,持久化,哨兵机制

    1 Redis事务 基本事务指令 Redis提供了一定的事务支持,可以保证一组操作原子执行不被打断,但是如果执行中出现错误,事务不能回滚,Redis未提供回滚支持. multi 开启事务 exec 执 ...

  3. C++之运算符_算数运算符

    运算符 **作用:**用于执行代码的运算 | **运算符类型** | **作用** || -------------- | -------------------------------------- ...

  4. PAT L2-021. 点赞狂魔 /// sort+unique去重

    https://www.patest.cn/contests/gplt/L2-021 题目大意: 微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持.每篇博文都有一些刻画其特性的标签,而你点赞 ...

  5. LightOJ 1341 - Aladdin and the Flying Carpet

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1341 题意:给你地毯面积和最小可能边的长度,让你求有几种组合的可能. 题解:这题就厉害 ...

  6. 转Git仓库分支(Branch)和标签(Tag)

    仓库的分支(Branch)规范,影响到每个团队的工作流的一致性:标签(Tag)便于开发团队.测 试团队和其他团队识别每个项目的版本,特别是在协同处理线上问题的时候,大家可以非常清楚 地知道线上运行版本 ...

  7. VMware 安装android-x86系统。

    首先先安装 VMware 虚拟机,并下载 android-x86_64-8.1-r2.iso 系统. VMware安装完成后,打开VMware Workstation,单击“创建新的虚拟机”,或者在菜 ...

  8. 笔记:简单的面向对象-web服务器

    import socket import re import multiprocessing import time import mini_frame class WSGIServer(object ...

  9. 4、Docker网络访问

    现在我们已经可以熟练的使用docker命令操作镜像和容器,并学会了如何进入到容器中去,那么实际的工作中,我们通常是在Docker中部署服务,我们需要在外部通过IP和端口进行访问的,那么如何访问到Doc ...

  10. [JZOJ1901] 【2010集训队出题】光棱坦克

    题目 题目大意 给你个平面上的一堆点,问序列\({p_i}\)的个数. 满足\(y_{p_{i-1}}>y_{p_i}\)并且\(x_{p_i}\)在\(x_{p_i-1}\)和\(x_{p_i ...